diff --git a/CHANGELOG.md b/CHANGELOG.md index f86eb77fa..bc9f5fa5e 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,7 +2,7 @@ # 🚀Changelog ------------------------------------------------------------------------------------------------------------- -# 5.8.35(2024-12-19) +# 5.8.35(2024-12-21) ### 🐣新特性 * 【poi 】 优化ExcelWriter中使用比较器writer的方法,只对第一条数据进行排序(pr#3807@Github) @@ -16,6 +16,7 @@ ### 🐞Bug修复 * 【crypto 】 修复JWTSignerUtil.createSigner中algorithmId未转换问题(issue#3806@Github) * 【core 】 修复DateUtil.rangeContains未重置问题(issue#IB8OFS@Gitee) +* 【cache 】 修复StampedCache类get方法并发问题(issue#IBCIQG@Gitee) ------------------------------------------------------------------------------------------------------------- # 5.8.34(2024-11-25) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java index 2e90c9c4c..b9971c315 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java @@ -88,7 +88,12 @@ public abstract class StampedCache extends AbstractCache { } /** - * 获取值 + * 获取值,使用乐观锁,但是此方法可能导致读取脏数据,但对于缓存业务可容忍。情况如下: + *
+	 *     1. 读取时无写入,不冲突,直接获取值
+	 *     2. 读取时无写入,但是乐观读时触发了并发异常,此时获取同步锁,获取新值
+	 *     4. 读取时有写入,此时获取同步锁,获取新值
+	 * 
* * @param key 键 * @param isUpdateLastAccess 是否更新最后修改时间 @@ -97,10 +102,24 @@ public abstract class StampedCache extends AbstractCache { */ private V get(K key, boolean isUpdateLastAccess, boolean isUpdateCount) { // 尝试读取缓存,使用乐观读锁 + CacheObj co = null; long stamp = lock.tryOptimisticRead(); - CacheObj co = getWithoutLock(key); - if (false == lock.validate(stamp)) { - // 有写线程修改了此对象,悲观读 + boolean isReadError = true; + if(lock.validate(stamp)){ + try{ + // 乐观读,可能读取脏数据,在缓存中可容忍,分两种情况 + // 1. 读取时无线程写入 + // 2. 读取时有线程写入,导致数据不一致,此时读取未更新的缓存值 + co = getWithoutLock(key); + isReadError = false; + } catch (final Exception ignore){ + // ignore + } + } + + if(isReadError){ + // 转换为悲观读 + // 原因可能为无锁读时触发并发异常,或者锁被占(正在写) stamp = lock.readLock(); try { co = getWithoutLock(key);