1. 程式人生 > >Redis(三) 構建鎖

Redis(三) 構建鎖

java

Redis鎖:

watchmultiexec組成的事務並不具有可擴展性,原因在於程序嘗試完成一個事務的時候,可能因為事務執行失敗而反復進行重試。

1. SETNX命令:只會在鍵不存在的情況下為鍵設置值,而鎖要做的就是將一個隨機生成的128UUID設置為鍵的值,並使用這個值來防止鎖被其他進程取得。如果程序在嘗試獲取鎖的時候失敗,那麽它將不斷進行重試,直到成功地取得鎖或超過給定的時間限為止;

代替了WATCH命令實現;解決了watch下操作競爭過多導致的延遲劇增問題;

實現方式:加鎖時,創建一個鍵為"lock:" + lockName,值為隨機UUID的鍵值對,只有當鍵值對創建成功時(被加鎖的

lockName沒有被其他線程使用)才返回,創建不成功則一直重試;釋放鎖時,watch命令監視要釋放的鎖,檢查鍵目前值是否和加鎖時的值一樣,一樣時刪除該鍵。

public String acquireLock(Jedis conn, String lockName, long acquireTimeout){

String identifier = UUID.randomUUID().toString();

long end = System.currentTimeMillis() + acquireTimeout;

while (System.currentTimeMillis() < end){

if (conn.setnx("lock:" + lockName, identifier) == 1){

return identifier;

}

try {

Thread.sleep(1);

}catch(InterruptedException ie){

Thread.currentThread().interrupt();

}

}

return null;

}

鎖的釋放:

public boolean releaseLock(Jedis conn, String lockName, String identifier) {

String lockKey = "lock:" + lockName;

while (true){

conn.watch(lockKey);

if (identifier.equals(conn.get(lockKey))){ //檢查進程是否仍然持有鎖

Transaction trans = conn.multi();

trans.del(lockKey);

List<Object> results = trans.exec();

if (results == null){

continue;

}

return true;

}

conn.unwatch();

break;

}

return false;

}

2. 細粒度鎖

只鎖住需要的部分。

在高負載情況下,使用鎖可以減少重試次數,降低延遲時間,提升性能並將加鎖的粒度調整至合適的大小。

3. 超時鎖

目前的鎖在持有者崩潰時不會自動釋放,導致鎖一直處於被獲取狀態;

給鎖加上超時限制特性,程序在獲得鎖後,調用EXPIRE命令為鎖設置過期時間;

public String acquireLockWithTimeout(

Jedis conn, String lockName, long acquireTimeout, long lockTimeout)

{

String identifier = UUID.randomUUID().toString();

String lockKey = "lock:" + lockName;

int lockExpire = (int)(lockTimeout / 1000);

long end = System.currentTimeMillis() + acquireTimeout;

while (System.currentTimeMillis() < end) {

if (conn.setnx(lockKey, identifier) == 1){

conn.expire(lockKey, lockExpire);

return identifier;

}

if (conn.ttl(lockKey) == -1) {

conn.expire(lockKey, lockExpire);

}

try {

Thread.sleep(1);

}catch(InterruptedException ie){

Thread.currentThread().interrupt();

}

}

// null indicates that the lock was not acquired

return null;

}

4. 計數信號量

計數信號量是一種鎖,它可以讓用戶限制一項資源能夠同時被多少個進程訪問,通常用於限定能夠同時使用的資源數量;(前面介紹的鎖都是只能被一個進程訪問的信號量);

使用ZSet有序集合存儲多個信號量持有者的信息:成員為進程在嘗試獲取信號量時生成的標識符;分值為當前時間戳;

過程:生成標識符;清除所有過期信號量;將新的標識符添加到zset;檢查新添標識符在zset中的排名;


Redis(三) 構建鎖