Redisson 分布式鎖機制
Redisson 是一個用於 Java 的分布式和分散式應用程序的高級框架,提供了多種分布式數據結構和服務。其中,分布式鎖是 Redisson 提供的重要功能之一。
分布式鎖的設計旨在在多個應用程序實例或多個線程之間實現協調,以確保只有一個實例(或線程)可以訪問關鍵區域,防止資源的併發競爭,確保數據的一致性和可靠性。
Redisson 分布式鎖的設計思想如下:
鎖的獲取與釋放: 使用
RLock
接口進行鎖的操作,可以通過tryLock
方法嘗試獲取鎖,並通過unlock
方法釋放鎖。鎖的可重入性: Redisson 支持可重入鎖,即同一個線程可以多次獲取同一把鎖,且每次獲取都需要對應的解鎖操作。
過期時間: 可以為每個鎖設置過期時間,避免因為應用程序異常導致鎖一直佔用。
阻塞與非阻塞:
tryLock
方法可以指定等待的時間,如果無法立即獲取到鎖,可以阻塞或者等待一段時間。監視狀態: Redisson 提供了監視機制,可以監視鎖的狀態,如果鎖被釋放,通知等待的實例進行競爭。
分佈式: Redisson 鎖是基於 Redis 實現的,因此可以實現分佈式環境下的鎖。
使用 Redisson 分布式鎖時需要注意以下幾點:
鎖的名稱應該是全局唯一的,以確保不同的實例可以協調。
鎖的獲取和釋放應該處於同一個代碼區塊內,確保鎖在正確的時機被釋放。
選擇適當的鎖的超時時間和等待策略,以避免死鎖和無限等待。
測試範例
import org.redisson.Redisson;
import org.redisson.api.RLock;
import org.redisson.api.RTopic;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.LongCodec;
import org.redisson.config.Config;
import java.time.LocalDateTime;
import java.util.List;
import java.util.concurrent.TimeUnit;
public class RedissonLockTest {
public static final String REDISSON_LOCK = "redisson";
public static void main(String[] args) throws InterruptedException {
RedissonClient redissonClient = redissonClient();
try {
RLock rLock = redissonClient.getLock(REDISSON_LOCK);
// 嘗試鎖定
rLock.tryLock();
printLog("using lock.");
// 創建一個新的線程
List.of(3,5).forEach(o -> newAThread(redissonClient, o));
// newAThread(redissonClient);
int second = 5;
printLog(String.format("sleep %s(s).", second));
TimeUnit.SECONDS.sleep(second);
// 如果當前線程鎖住並且持有該鎖
if (rLock.isLocked() && rLock.isHeldByCurrentThread())
rLock.unlock();
printLog("release lock success.");
} catch (Exception e) {
e.printStackTrace();
} finally {
TimeUnit.SECONDS.sleep(20);
// 關閉 Redisson 客戶端
redissonClient.shutdown();
}
TimeUnit.SECONDS.sleep(2);
System.exit(0);
}
public static void newAThread(RedissonClient redissonClient, int i) {
new Thread(() -> {
try {
// 創建一個 Redisson 分布式鎖
RLock anotherLock = redissonClient.getLock(REDISSON_LOCK);
// 創建一個 Redisson 訂閱主題
RTopic topic = redissonClient.getTopic("redisson_lock__channel:{redisson}", LongCodec.INSTANCE);
// RPatternTopic patternTopic = redissonClient.getPatternTopic("*", LongCodec.INSTANCE); // 亦可使用Regex 方式
// 添加訂閱者到訂閱主題
int topicId = topic.addListener(Long.class, (channel, msg) -> printLog(String.format("RTopic => channel:%s, message:%s", channel, msg)));
// 嘗試鎖住
// boolean lockSuccess = anotherLock.tryLock(2, TimeUnit.SECONDS);
boolean lockSuccess = anotherLock.tryLock(5, 1, TimeUnit.SECONDS); // 等待時間內收到釋放鎖消息,則重新去競爭鎖
// boolean lockSuccess = anotherLock.tryLock(2, 1, TimeUnit.SECONDS); // 等待時間內未獲得鎖直接回傳結果。
// boolean lockSuccess = anotherLock.tryLock(); // 未設置等待時間,則直接判斷當前是否取到鎖
if (lockSuccess) {
printLog("try lock success.");
TimeUnit.SECONDS.sleep(i);
// 如果當前線程鎖住並且持有該鎖
if (anotherLock.isLocked() && anotherLock.isHeldByCurrentThread()) {
anotherLock.unlock();
printLog("release lock success.");
}
} else {
printLog("try lock failed.");
}
// 移除訂閱者
topic.removeListener(topicId);
} catch (Exception e) {
e.printStackTrace();
}
}, "Thread_" + i).start();
}
static void printLog(String message) {
System.out.println(String.format("%s - [%s] -> %s", LocalDateTime.now(), Thread.currentThread().getName(), message));
}
private static RedissonClient redissonClient() {
Config config = new Config();
config.useSingleServer().setAddress("redis://172.20.160.120:6379");
config.setLockWatchdogTimeout(TimeUnit.SECONDS.toMillis(10));
return Redisson.create(config);
}
}
總而言之,Redisson 提供了方便易用的 API 來實現分布式鎖,這有助於處理多實例或多線程的併發問題,確保數據操作的一致性和可靠性。
Last updated