架構知識使用Redis的分佈式Java鎖

通過優銳課核心java學習筆記中,我們可以看到,碼了很多專業的相關知識, 分享給大家參考學習。

瞭解有關分佈式鎖定以及如何在項目中實現它的更多信息!

什麼是分佈式鎖定?

在多線程程序中,不同的線程可能需要訪問相同的資源。但是,允許所有線程同時訪問資源可能導致爭用情況,錯誤和其他意外行爲。

爲了確保沒有兩個線程可以同時訪問同一資源,並確保以可預測的順序對資源進行操作,程序員使用一種稱爲鎖的機制。每個線程首先獲取鎖,然後對資源進行操作,最後將鎖釋放給其他線程。

在Java中,由於多種原因,鎖定對象通常比使用同步塊更靈活。首先,Lock API可以以不同的方法運行,而同步塊完全包含在一個方法中。

另外,如果線程被阻止,則無法訪問同步的塊。使用Lock,該線程將僅在可用時獲取鎖。這大大減少了線程等待的時間。另外,當線程正在等待時,可以調用一種方法來中斷線程,而當線程正在等待獲取同步塊時,這是不可能的。

分佈式鎖定意味着不僅需要考慮多個線程或進程,還需要考慮在不同計算機上運行的不同客戶端。這些單獨的服務器必須進行協調,以確保它們中的任何一個在任何給定時間都在使用資源。

基於Redis的分佈式Java鎖定工具

Redisson框架是用於Java的基於Redis的內存數據網格,可爲需要執行分佈式鎖定的程序員提供多個對象。 下面,我們將討論每個選項及其之間的區別。

RLock接口在Java中實現java.util.concurrent.locks.Lock接口。 這是一個可重入鎖,這意味着線程可以多次鎖定資源。 一個計數器變量跟蹤鎖定請求被執行了多少次。 一旦線程發出足夠的解鎖請求並且計數器達到0,資源便被釋放。

以下簡單代碼示例演示瞭如何在Redisson中創建和初始化Lock:

RLock lock = redisson.getLock("anyLock");

// Most familiar locking method

lock.lock();

try {

  ...

} finally {

  lock.unlock();

}

 

如果獲取此鎖的Redisson實例崩潰,則該鎖可能會在此獲取狀態下永久掛起。 爲了避免這種情況,Redisson維護了一個鎖“看門狗”,該“看門狗”會在持有該鎖的Redisson實例仍處於活動狀態時延長該鎖的過期時間。 默認情況下,此鎖定看門狗的超時爲30秒。 可以通過Config.lockWatchdogTimeout設置更改此限制。

Redisson還允許在獲取租約時指定leaseTime參數。 在指定的時間間隔後,鎖將自動釋放:

// Acquire lock and release it automatically after 10 seconds

// if unlock method hasn't been invoked

lock.lock(10, TimeUnit.SECONDS);

// Wait for 100 seconds and automatically unlock it after 10 seconds

boolean res = lock.tryLock(100, 10, TimeUnit.SECONDS);

if (res) {

   try {

     ...

   } finally {

       lock.unlock();

   }

}

 

Redisson還爲Lock對象提供了異步/響應/ rxjava2接口:

RLock lock = redisson.getLock("anyLock");

lock.lockAsync();

...

// Reactive Stream (Spring Project Reactor implementation)

RLockReactive lock = redissonReactive.getLock("anyLock");

Mono<Void> res = lock.lock();

...

// Reactive Stream (RxJava2 implementation)

RLockReactive lock = redissonRx.getLock("anyLock");

Flowable<Void> res = lock.lock();

...

 

由於RLock實現了Lock接口,因此只有擁有鎖的線程才能解鎖資源。 否則,任何嘗試都會遇到IllegalMonitorStateException。

2. FairLock

與其表兄弟RLock一樣,RFairLock也實現了java.util.concurrent.locks.Lock接口。 通過使用FairLock,可以保證線程將按照請求資源的順序來獲取資源(即``先進先出''隊列)。 在爲隊列中的下一個線程解鎖資源之前,Redisson會給已死掉五秒鐘的線程重新啓動。

與RLocks一樣,創建和啓動FairLock是一個簡單的過程:

RLock lock = redisson.getFairLock("anyLock");

lock.lock();

try {

  ...

} catch {

  lock.unlock();

}

 

3. ReadWriteLock

 Redisson的RReadWriteLock實現了java.util.concurrent.locks.ReadWriteLock接口。 在Java中,讀/寫鎖實際上是兩個鎖的組合:一個只讀鎖可以同時由多個線程擁有,而寫鎖只能一次由一個線程擁有。

創建和初始化RReadWriteLock的方法如下:

RReadWriteLock rwlock = redisson.getReadWriteLock("anyRWLock");

rwlock.readLock().lock();

try {

  ...

} finally {

  rwlock.readLock().lock();

}

rwlock.writeLock().lock();

try {

  ...

} finally {

  rwlock.writeLock().lock();

}

 

4. RedLock

RedissonRedLock對象實現Redlock鎖定算法,以將分佈式鎖與Redis一起使用:

RLock lock1 = redissonInstance1.getLock("lock1");

RLock lock2 = redissonInstance2.getLock("lock2");

RLock lock3 = redissonInstance3.getLock("lock3");

RedissonRedLock lock = new RedissonRedLock(lock1, lock2, lock3);

lock.lock();

try {

  ...

} finally {

   lock.unlock();

}

 

在Redlock算法中,我們在單獨的計算機或虛擬機上有許多獨立的Redis主節點。 該算法嘗試使用相同的鍵名和隨機值依次獲取這些實例中的每個實例的鎖。 僅當客戶端能夠比鎖有效的總時間更快地從大多數實例中獲取鎖時,才獲取鎖。

5.多重鎖

RedissonMultiLock對象能夠將多個單獨的RLock實例組合在一起並作爲單個實體進行管理:

RLock lock1 = redissonInstance1.getLock("lock1");

RLock lock2 = redissonInstance2.getLock("lock2");

RLock lock3 = redissonInstance3.getLock("lock3");

RedissonMultiLock lock = new RedissonMultiLock(lock1, lock2, lock3);

lock.lock();

try {

  ...

} finally {

  lock.unlock();

}

 

正如我們在上面的示例中看到的那樣,每個RLock對象可能屬於不同的Redisson實例。 反過來,這可能會連接到其他Redis數據庫。

最後的想法

在本文中,我們探討了Java開發人員可用於在Redis數據庫之上的Redisson框架中執行分佈式鎖定的一些不同工具:Lock,FairLock,ReadWriteLock,RedLock和MultiLock。 有關Redisson中分佈式計算的更多信息,請遵循GitHub上的項目Wiki。

> 喜歡這篇文章的可以點個贊,歡迎大家留言評論,記得關注我,每天持續更新技術乾貨、職場趣事、海量面試資料等等
 > 如果你對java技術很感興趣也可以交流學習,共同學習進步。 
> 不要再用"沒有時間“來掩飾自己思想上的懶惰!趁年輕,使勁拼,給未來的自己一個交代

文章寫道這裏,歡迎完善交流。最後奉上近期整理出來的一套完整的java架構思維導圖,分享給大家對照知識點參考學習。有更多JVM、Mysql、Tomcat、Spring Boot、Spring Cloud、Zookeeper、Kafka、RabbitMQ、RockerMQ、Redis、ELK、Git等Java乾貨

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章