redis分布式鎖詳解Redisson(RedissonClient)
RedissonClient中提供了好多種鎖,還有其它很多實用的方法。
Redisson是Redis官方推薦的Java版的Redis客戶端。實現(xiàn)了對數(shù)據(jù)的增刪改查等操作。
Redisson實現(xiàn)了RedissonClient的接口。這里只介紹其中的鎖。
依賴
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>3.10.7</version>
</dependency>重入鎖 RedissonLock
重入鎖可以通過Redisson的getLock方法獲取
@Override
public RLock getLock(String name) {
return new RedissonLock(connectionManager.getCommandExecutor(), name);
}/**
* 獲取鎖-同一個線程可重入
* @param lockKey 鎖的名稱
* @param waitTime 獲取鎖的等待時間
* @param leaseTime 鎖的持續(xù)時間
* @param unit 時間的單位
* @return 獲取鎖的結(jié)果
*/
public Boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 1. 最常見的使用方法
//lock.lock();
// 2. 支持過期解鎖功能,10秒鐘以后自動解鎖, 無需調(diào)用unlock方法手動解鎖
//lock.lock(10, TimeUnit.SECONDS);
boolean locked = lock.tryLock(waitTime, leaseTime, unit);
if (locked) lockKeys.add(lockKey);
return locked;
} catch (InterruptedException e) {
System.out.println(String.format("嘗試獲取鎖%s失敗", lockKey));
e.printStackTrace();
}
return Boolean.FALSE;
}
/**
* 解鎖 - 重入的方式,所以同一個線程加了幾次鎖就要釋放幾次鎖
* @param lockKey 鎖的值
*/
public boolean unLock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
if (null != lock && lock.isHeldByCurrentThread()) { //判斷鎖是否存在,和是否當(dāng)前線程加的鎖。
lock.unlock();
return lockKeys.remove(lockKey);
}
} catch (Exception e) {
System.out.println(String.format("解鎖鎖%s失敗", lockKey));
e.printStackTrace();
}
return false;
}重入鎖的異步執(zhí)行方式
/**
* 異步獲取鎖-同一個線程可重入
* @param lockKey 鎖的名稱
* @param waitTime 獲取鎖的等待時間
* @param leaseTime 鎖的持續(xù)時間
* @param unit 時間的單位
* @return 獲取鎖的結(jié)果
*/
public Boolean tryLockAsync(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getLock(lockKey);
try {
// 1. 最常見的使用方法
//lock.lockAsync();
// 2. 支持過期解鎖功能,10秒鐘以后自動解鎖, 無需調(diào)用unlock方法手動解鎖
//lock.lockAsync(10, TimeUnit.SECONDS);
RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
if (locked.get()) lockKeys.add(lockKey);
return locked.get();
} catch (InterruptedException | ExecutionException e) {
System.out.println(String.format("嘗試獲取鎖%s失敗", lockKey));
e.printStackTrace();
}
return Boolean.FALSE;
}
/**
* 解鎖 - 重入的方式,所以同一個線程加了幾次鎖就要釋放幾次鎖
* @param lockKey 鎖的值
*/
public boolean unAsyncLock(String lockKey) {
try {
RLock lock = redissonClient.getLock(lockKey);
if (null != lock && lock.isHeldByCurrentThread()) { //判斷鎖是否存在,和是否當(dāng)前線程加的鎖。
RFuture<Void> future = lock.unlockAsync();
if(future.await(5 * 1000) && future.isSuccess()) {
return lockKeys.remove(lockKey);
}
}
} catch (Exception e) {
System.out.println(String.format("解鎖%s失敗", lockKey));
e.printStackTrace();
}
return false;
}公平鎖
改公平鎖是可重入的,在提供了自動過期解鎖功能的同時,保證了當(dāng)多個Redisson客戶端線程同時請求加鎖時,優(yōu)先分配給先發(fā)出請求的線程。同時也提供了異步的方式。
實現(xiàn)方式參照上一個鎖的實現(xiàn)。
@Override
public RLock getFairLock(String name) {
return new RedissonFairLock(connectionManager.getCommandExecutor(), name);
}/**
* 公平鎖
*
* @param lockKey 鎖的名稱
* @param waitTime 獲取鎖的等待時間
* @param leaseTime 鎖的持續(xù)時間
* @param unit 時間的單位
*
* @return 獲取鎖的結(jié)果
*/
public Boolean tryFairLock(String lockKey, long waitTime, long leaseTime, TimeUnit unit) {
RLock lock = redissonClient.getFairLock(lockKey);
try {
// 1. 最常見的使用方法
//lock.tryLock();
// 2. 支持過期解鎖功能,10秒鐘以后自動解鎖, 無需調(diào)用unlock方法手動解鎖
//lock.tryLock(10, TimeUnit.SECONDS);
boolean locked = lock.tryLock(waitTime, leaseTime, unit);
if (locked)
lockKeys.add(lockKey);
return locked;
/* 異步實現(xiàn)方式
lock.lockAsync();
lock.lockAsync(10, TimeUnit.SECONDS);
RFuture<Boolean> locked = lock.tryLockAsync(waitTime, leaseTime, unit);
if (locked.get()) lockKeys.add(lockKey);
return locked.get();*/
} catch (InterruptedException e) {
System.out.println(String.format("嘗試獲取鎖%s失敗", lockKey));
e.printStackTrace();
}
return Boolean.FALSE;
}
/**
* 解鎖 - 重入的方式,所以同一個線程加了幾次鎖就要釋放幾次鎖
*
* @param lockKey 鎖的值
*/
public boolean unFairLock(String lockKey) {
try {
RLock lock = redissonClient.getFairLock(lockKey);
if (null != lock && lock.isHeldByCurrentThread()) { //判斷鎖是否存在,和是否當(dāng)前線程加的鎖。
lock.unlock();
return lockKeys.remove(lockKey);
//異步方式刪除鎖
/*RFuture<Void> future = lock.unlockAsync();
if (future.await(5 * 1000) && future.isSuccess()) {
return lockKeys.remove(lockKey);
}*/
}
} catch (Exception e) {
System.out.println(String.format("解鎖%s失敗", lockKey));
e.printStackTrace();
}
return false;
}聯(lián)鎖(MultiLock)
Redisson的RedissonMultiLock對象可以將多個RLock對象關(guān)聯(lián)為一個聯(lián)鎖,每個RLock對象實例可以來自于不同的Redisson實例。
需要注意的是,在鎖的釋放時,可以單獨釋放Redisson添加的鎖,其他鎖不會釋放依舊存在。
/**
* 連鎖-只有所有的RedissonClient都鎖成功才算成功
*
* @param waitTime 獲取鎖的等待時間
* @param leaseTime 鎖的持續(xù)時間
* @param unit 時間的單位
*
* @return 獲取鎖的結(jié)果
*/
public Boolean tryMultiLock(RedissonClient redisson1,RedissonClient redisson2, long waitTime, long leaseTime, TimeUnit unit){
RLock lock1 = redisson1.getLock("zhong:test:lock1");
RLock lock2 = redisson2.getLock("zhong:test:lock2");
RLock lock = redissonClient.getMultiLock(lock1, lock2);
try {
// 同時加鎖:lock1 lock2 lock3, 所有的鎖都上鎖成功才算成功。
lock.lock();
// 嘗試加鎖,最多等待100秒,上鎖以后10秒自動解鎖
boolean res = lock.tryLock(waitTime, unit);
return res;
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
return false;
}
/**
* 連鎖 - 需要遵循誰加的鎖設(shè)計釋放鎖,可以單獨釋放自己加的鎖
*
*/
public boolean unMultiLock(RedissonClient client ,RedissonClient client1) {
try {
List<RLock> locks = new ArrayList<>();
locks.add(client.getLock("zhong:test:lock1"));
locks.add(client1.getLock("zhong:test:lock2"));
RedissonMultiLock lock = new RedissonMultiLock(locks.toArray(new RLock[0]));
lock.unlock();
//異步方式刪除鎖
/*RFuture<Void> future = lock.unlockAsync();
if (future.await(5 * 1000) && future.isSuccess()) {
return lockKeys.remove(lockKey);
}*/
} catch (Exception e) {
System.out.println(String.format("解鎖失敗"));
e.printStackTrace();
return false;
}
return true;
}RedissonClient還提供了紅鎖,讀寫鎖等。
在實際應(yīng)用中我們最常用的分布式鎖一般都是設(shè)置定時過期的。
這樣的鎖在實際應(yīng)用中存在一個問題就是服務(wù)宕機(jī)或重啟這個鎖在redis上是一直存在的。一旦重啟就可能會導(dǎo)致所有線程無法獲取到鎖。
解決辦法就是在加鎖的時候?qū)㈡i記錄到Set里面。釋放鎖的時候?qū)⒂涗汼et中的鎖刪除,在服務(wù)停止之前就就根據(jù)set記錄里面的鎖先將歐鎖釋放。
這樣就能保證重啟后能獲取到鎖。實現(xiàn)方式參考DisposableBean的實現(xiàn)方式。
總結(jié)
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
MybatisPlus多條件?or()的使用問題小結(jié)
這篇文章主要介紹了MybatisPlus多條件?or()的使用問題小結(jié),本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-05-05
Java 中比較兩個 long 類型變量大小的方法(實例詳解)
比較兩個long類型變量的大小時,由于是基本數(shù)據(jù)類型,直接使用Java 內(nèi)置的關(guān)系運算符即可,這些運算符比較的是變量的實際值,而非內(nèi)存地址,下面給大家介紹Java中比較兩個long類型變量大小的方法,感興趣的朋友一起看看吧2025-06-06
Java聊天室之實現(xiàn)接收和發(fā)送Socket
這篇文章主要為大家詳細(xì)介紹了Java簡易聊天室之實現(xiàn)接收和發(fā)送Socket功能,文中的示例代碼講解詳細(xì),具有一定的借鑒價值,需要的可以了解一下2022-10-10
基于CyclicBarrier和CountDownLatch的使用區(qū)別說明
這篇文章主要介紹了基于CyclicBarrier和CountDownLatch的使用區(qū)別說明,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-09-09
(starters)springboot-starter整合阿里云datahub方式
這篇文章主要介紹了(starters)springboot-starter整合阿里云datahub方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11
Java操作數(shù)據(jù)庫(行級鎖,for update)
這篇文章主要介紹了Java操作數(shù)據(jù)庫(行級鎖,for update),文章圍繞Java操作數(shù)據(jù)庫的相關(guān)資料展開詳細(xì)內(nèi)容,需要的小伙伴可以參考一下,希望對你有所幫助2021-12-12
使用@Value為靜態(tài)變量導(dǎo)入并使用導(dǎo)入的靜態(tài)變量進(jìn)行初始化方式
這篇文章主要介紹了使用@Value為靜態(tài)變量導(dǎo)入并使用導(dǎo)入的靜態(tài)變量進(jìn)行初始化方式,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2023-02-02

