From 7467d03fe27fc67f4e1ddd4ac7b1f285f7a8f069 Mon Sep 17 00:00:00 2001 From: Looly Date: Sat, 21 Dec 2024 00:46:42 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8DStampedCache=E7=B1=BBget?= =?UTF-8?q?=E6=96=B9=E6=B3=95=E5=B9=B6=E5=8F=91=E9=97=AE=E9=A2=98=EF=BC=88?= =?UTF-8?q?issue#IBCIQG@Gitee=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 3 ++- .../cn/hutool/cache/impl/StampedCache.java | 27 ++++++++++++++++--- 2 files changed, 25 insertions(+), 5 deletions(-) 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);