基於redis的分布式鎖(Java實現)

來源:互聯網
上載者:User
Github源碼:

github.com/z521598/redis-lock

實現原理:

1.setnx

Redis的setnx指令(文檔參考),setnx意為SET if Not eXists,命令格式:setnx $key $value

如果此key不存在,則設定值為value,傳回值為1;如果此key存在,則不設定,傳回值為0。如:

127.0.0.1:6379[1]> setnx key v(integer) 1127.0.0.1:6379[1]> setnx key v2(integer) 0

redis是單線程的,是安全執行緒的,setnx指令由於上述的特效能夠滿足高並發情況下的對於鎖的需求。

2.SpringData-redis

springData-redis是向Redis發送命令以及接受資料的高層次抽象的模版方法,簡單理解為“使用java向redis發送命令以及接受資料的用戶端”(文檔參考)

設定檔:github.com/z521598/redis-lock/blob/master/src/main/resources/applicationContext.xml

V1說明:

最簡單的最粗糙的鎖實現,實現了2個方法。

方法1:擷取鎖,public UUID acquire(String lockKey, long acquireTimeoutInMillis, long lockExpiryInMillis)

參數說明:lockKey為鎖的key;acquireTimeoutInMillis為擷取鎖的等待時間,如果超過此時間就放棄鎖;lockExpiryInMillis為鎖的到期時間。

傳回值:鎖對應的值,就是命令中“setnx key value”的value。

思路:

1.調用SpringData-Redis的setIfAbsent(lockKey, value)方法(就是命令列中的setnx),value為隨機的UUID。

2.如果返回true,則說明已經擷取的鎖,則繼續設定逾時時間,返回設定的UUID。

  如果返回false,則說明未擷取到鎖,則休眠100ms,並記錄總休眠的時間,如果lockExpiryInMillis大於等於記錄總休眠的時間,則說明未擷取到鎖,返回null。

方法2:釋放鎖,public void release(String lockKey, UUID uuid)

參數說明:lockKey為鎖的key;uuid為鎖的value。

思路:檢查鎖的value是否為uuid,如果相等,則釋放,如果不相等,則什麼都不做;防止釋放了其他線程擷取的鎖。

明顯的缺點:

第一步,擷取鎖,第二步,然後設定逾時時間。這是2步操作,不是原子性操作,如果第一步操作之後,程式崩潰了或者掉電了或者redis恰巧進行了主從切換等等原因,第二步無法正常執行,這樣這個鎖就永遠得不到釋放。

代碼:
public UUID acquire(String lockKey, long acquireTimeoutInMillis, long lockExpiryInMillis)            throws InterruptedException {        UUID uuid = UUID.randomUUID();        long timeout = 0L;        while (timeout < acquireTimeoutInMillis) {            if (redisTemplate.opsForValue().setIfAbsent(lockKey, uuid.toString())) {                redisTemplate.expire(lockKey, lockExpiryInMillis, TimeUnit.MILLISECONDS);                return uuid;            }            TimeUnit.MILLISECONDS.sleep(DEFAULT_ACQUIRE_RESOLUTION_MILLIS);            timeout += DEFAULT_ACQUIRE_RESOLUTION_MILLIS;        }        return null;    }

鎖實現:github.com/z521598/redis-lock/blob/master/src/main/java/com/redis/lock/sdata/v1/LockService.java

單元測試:github.com/z521598/redis-lock/blob/master/src/test/java/com/redis/lock/sdata/v1/LockServiceTest.java

V2說明:

大體與V1相同,但是鎖的value是"到期的時間",如果擷取鎖的時候,發現到期時間小於now,則視為鎖已經到期。

缺點:(極少出現的情況)

當redis是主從形式的情況下,擷取鎖之後,master宕機,slave接管,但是這個時候“新master”還未同步鎖的key。在這個時候,其他線程去擷取鎖,發現無此key,則擷取了不應該擷取的鎖,這樣就會引起不安全的情況。

代碼:

鎖實現:github.com/z521598/redis-lock/tree/master/src/main/java/com/redis/lock/sdata/v2

單元測試:github.com/z521598/redis-lock/blob/master/src/test/java/com/redis/lock/sdata/v2/LockV2ServiceTest.java

 V3

敬請期待

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.