分布式鎖的簡單實作代碼:
/**
* 分布式鎖的簡單實作代碼
* Created by liuyang on 2017/4/20.
*/
public class DistributedLock {
private final JedisPool jedisPool;
public DistributedLock(JedisPool jedisPool) {
this.jedisPool = jedisPool;
}
/**
* 加鎖
* @param lockName 鎖的key
* @param acquireTimeout 擷取逾時時間
* @param timeout 鎖的逾時時間
* @return 鎖辨別
*/
public String lockWithTimeout(String lockName, long acquireTimeout, long timeout) {
Jedis conn = null;
String retIdentifier = null;
try {
// 擷取連接配接
conn = jedisPool.getResource();
// 随機生成一個value
String identifier = UUID.randomUUID().toString();
// 鎖名,即key值
String lockKey = "lock:" + lockName;
// 逾時時間,上鎖後超過此時間則自動釋放鎖
int lockExpire = (int) (timeout / );
// 擷取鎖的逾時時間,超過這個時間則放棄擷取鎖
long end = System.currentTimeMillis() + acquireTimeout;
while (System.currentTimeMillis() < end) {
if (conn.setnx(lockKey, identifier) == ) {
conn.expire(lockKey, lockExpire);
// 傳回value值,用于釋放鎖時間确認
retIdentifier = identifier;
return retIdentifier;
}
// 傳回-1代表key沒有設定逾時時間,為key設定一個逾時時間
if (conn.ttl(lockKey) == -) {
conn.expire(lockKey, lockExpire);
}
try {
Thread.sleep();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
} catch (JedisException e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.close();
}
}
return retIdentifier;
}
/**
* 釋放鎖
* @param lockName 鎖的key
* @param identifier 釋放鎖的辨別
* @return
*/
public boolean releaseLock(String lockName, String identifier) {
Jedis conn = null;
String lockKey = "lock:" + lockName;
boolean retFlag = false;
try {
conn = jedisPool.getResource();
while (true) {
// 監視lock,準備開始事務
conn.watch(lockKey);
// 通過前面傳回的value值判斷是不是該鎖,若是該鎖,則删除,釋放鎖
if (identifier.equals(conn.get(lockKey))) {
Transaction transaction = conn.multi();
transaction.del(lockKey);
List<Object> results = transaction.exec();
if (results == null) {
continue;
}
retFlag = true;
}
conn.unwatch();
break;
}
} catch (JedisException e) {
e.printStackTrace();
} finally {
if (conn != null) {
conn.close();
}
}
return retFlag;
}
}
測試剛才實作的分布式鎖
例子中使用50個線程模拟秒殺一個商品,使用–運算符來實作商品減少,從結果有序性就可以看出是否為加鎖狀态。
模拟秒殺服務,在其中配置了jedis線程池,在初始化的時候傳給分布式鎖,供其使用。
/**
* Created by liuyang on 2017/4/20.
*/
public class Service {
private static JedisPool pool = null;
private DistributedLock lock = new DistributedLock(pool);
int n = ;
static {
JedisPoolConfig config = new JedisPoolConfig();
// 設定最大連接配接數
config.setMaxTotal();
// 設定最大空閑數
config.setMaxIdle();
// 設定最大等待時間
config.setMaxWaitMillis( * );
// 在borrow一個jedis執行個體時,是否需要驗證,若為true,則所有jedis執行個體均是可用的
config.setTestOnBorrow(true);
pool = new JedisPool(config, "127.0.0.1", , );
}
public void seckill() {
// 傳回鎖的value值,供釋放鎖時候進行判斷
String identifier = lock.lockWithTimeout("resource", , );
System.out.println(Thread.currentThread().getName() + "獲得了鎖");
System.out.println(--n);
lock.releaseLock("resource", identifier);
}
}
模拟線程進行秒殺服務:
public class ThreadA extends Thread {
private Service service;
public ThreadA(Service service) {
this.service = service;
}
@Override
public void run() {
service.seckill();
}
}
public class Test {
public static void main(String[] args) {
Service service = new Service();
for (int i = ; i < ; i++) {
ThreadA threadA = new ThreadA(service);
threadA.start();
}
}
}
結果如下,結果為有序的:

若注釋掉使用鎖的部分:
public void seckill() {
// 傳回鎖的value值,供釋放鎖時候進行判斷
//String indentifier = lock.lockWithTimeout("resource", , );
System.out.println(Thread.currentThread().getName() + "獲得了鎖");
System.out.println(--n);
//lock.releaseLock("resource", indentifier);
}
從結果可以看出,有一些是異步進行的: