目錄
Redisson
整合Redisson
?RLock
RReadWriteLock
RSemaphore
RCountDownLatch
優化三級分類緩存
緩存一致性問題
雙寫模式
失效模式
臟數據解決
Redisson
提供redis分布式鎖(Distributed locks with Redis)的java客戶端
整合Redisson
引入
<dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.12.0</version></dependency>
程序化配置
@Configuration
public class RedissonConfig {@Value("${spring.redis.host}"+":"+"${spring.redis.port}")private String singleAddress;@Bean(destroyMethod = "shutdown")public RedissonClient redisson() throws IOException {Config config = new Config();/*SingleServer() 單節點模式redis:// redis 連接協議*/config.useSingleServer().setAddress("redis://"+singleAddress);return Redisson.create(config);}
}
?RLock
可重入鎖(Reentrant Lock)它允許同一個線程多次獲取同一把鎖而不會產生死鎖。
RLock lock = redisson.getLock("myLock");// 嘗試獲取鎖,最多等待10秒,鎖有效期30秒
boolean res = lock.tryLock(10, 30, TimeUnit.SECONDS);
if (res) {try {// 業務代碼} finally {lock.unlock();}
}
-
可重入性:同一線程可多次獲取同一把鎖
-
鎖續期:內置看門狗機制自動續期
-
公平鎖:支持公平鎖和非公平鎖
-
鎖釋放:確保只有鎖持有者能釋放鎖
RReadWriteLock
讀寫鎖,它允許多個讀操作同時進行,但寫操作是排他的。
RReadWriteLock rwLock = redisson.getReadWriteLock("myReadWriteLock");
RLock readLock = rwLock.readLock(); // 獲取讀鎖
RLock writeLock = rwLock.writeLock(); // 獲取寫鎖
writeLock.lock(); // 先獲取寫鎖
try {// 寫操作...// 保持寫鎖的同時獲取讀鎖(鎖降級)readLock.lock();try {// 讀操作...} finally {// 注意:這里不能釋放寫鎖}// 可以繼續持有寫鎖做其他操作
} finally {writeLock.unlock(); // 最后釋放寫鎖// 此時仍持有讀鎖
}
-
讀寫分離:
-
多個線程可以同時持有讀鎖
-
寫鎖是排他的,有寫鎖時不能有讀鎖或其他寫鎖
-
-
可重入性:
-
讀鎖和寫鎖都支持可重入
-
同一線程可以多次獲取同一把讀鎖或寫鎖
-
-
鎖降級:
-
支持將寫鎖降級為讀鎖
-
但不支持讀鎖升級為寫鎖(會死鎖)
-
-
公平性選擇:
-
支持公平和非公平兩種模式
-
RSemaphore
?信號量,它允許多個線程/進程在分布式環境中協調對共享資源的訪問
// 獲取信號量實例(初始許可數為5)
RSemaphore semaphore = redisson.getSemaphore("mySemaphore");
semaphore.trySetPermits(5); // 初始化許可數量// 獲取1個許可(阻塞直到可用)
semaphore.acquire();try {// 訪問受限資源accessLimitedResource();
} finally {// 釋放許可semaphore.release();
}
-
資源限制:控制同時訪問特定資源的線程/進程數量
-
分布式支持:跨JVM、跨服務器的協調能力
-
公平性選擇:支持公平和非公平兩種模式
-
可重入:支持同一線程多次獲取許可
-
超時機制:支持嘗試獲取許可的超時設置
典型應用場景
-
限流控制:限制系統并發請求數
-
資源池管理:如數據庫連接池控制
-
任務調度:限制同時執行的任務數量
-
API訪問限制:控制第三方API調用頻率
RCountDownLatch
閉鎖,它允許一個或多個線程等待其他線程完成操作后再繼續執行。
RCountDownLatch latch = redisson.getCountDownLatch("myLatch");// 初始化計數器為5
latch.trySetCount(5);// 減少計數器(每個工作線程完成后調用)
latch.countDown();//獲取當前計數
long remaining = latch.getCount();// 等待計數器歸零(阻塞)
latch.await();// 帶超時的等待
boolean reached = latch.await(10, TimeUnit.SECONDS);
-
一次性使用:計數器歸零后不能重置(與JDK的CountDownLatch一致)
-
等待/通知機制:線程可以等待計數器歸零
-
可視化監控:可通過Redis直接查看當前計數狀態
典型應用場景
-
分布式任務同步:等待多個分布式任務完成
-
系統初始化:等待所有服務初始化完成
-
批量處理:等待所有子任務處理完成
-
測試協調:分布式測試中的線程協調
優化三級分類緩存
@Overridepublic Map<String, List<Level2CategoryVo>> getLevel2AndLevel3Category() {//1.先從緩存中獲取 catalogJsonValueOperations<String,String> valueOperations = redisTemplate.opsForValue();String catalogJson = valueOperations.get("catalogJson");Map<String, List<Level2CategoryVo>> res = null;if(StringUtils.isEmpty(catalogJson)){//緩存中無對應數據,查詢數據庫res = getCatalogJsonFromDBWithRedissonLock();}else{res = JSON.parseObject(catalogJson,new TypeReference<Map<String, List<Level2CategoryVo>> >(){});}return res;}/*** 獲取 Redisson 分布式鎖,查詢數據庫* @return*/private Map<String, List<Level2CategoryVo>> getCatalogJsonFromDBWithRedissonLock(){Map<String, List<Level2CategoryVo>> res = null;RLock lock = redisson.getLock("CatalogJson-Lock");lock.lock();//成功獲取到鎖try {res = getCatalogJsonFromDB();}finally {lock.unlock();}return res;}
緩存一致性問題
雙寫模式
修改數據庫,并修改緩存。
失效模式
修改數據庫后,刪除緩存,下一次查詢時緩存數據。
臟數據解決
倆種模式都會產生暫時的臟數據,解決方案:
- 讀寫鎖
- 過期時間,保證最終一致性
- 一致性要求高的數據,不存入緩存,應直接查詢數據庫