diff --git a/CHANGELOG.md b/CHANGELOG.md index dd5b7e057..38f80ae28 100755 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ * 【core 】 修复Convert.digitToChinese(0)输出金额无`元整问题`(issue#3662@Github) * 【core 】 修复CsvParser中对正文中双引号处理逻辑问题(pr#1244@Gitee) * 【core 】 修复ZipUtil.zip压缩到本目录时可能造成的死循环问题(issue#IAGYDG@Gitee) +* 【cache 】 修复AbstractCache.get中锁不一致导致的并发问题(issue#3686@Github) ------------------------------------------------------------------------------------------------------------- # 5.8.29(2024-07-03) diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java index 281cdbf60..3465e9f00 100755 --- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java +++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java @@ -2,7 +2,6 @@ package cn.hutool.cache.impl; import cn.hutool.cache.Cache; import cn.hutool.cache.CacheListener; -import cn.hutool.core.exceptions.ExceptionUtil; import cn.hutool.core.lang.func.Func0; import cn.hutool.core.lang.mutable.Mutable; import cn.hutool.core.lang.mutable.MutableObj; @@ -130,18 +129,13 @@ public abstract class AbstractCache implements Cache { keyLock.lock(); try { // 双重检查锁,防止在竞争锁的过程中已经有其它线程写入 - final CacheObj co = getWithoutLock(key); - if (null == co || co.isExpired()) { - try { - v = supplier.call(); - } catch (Exception e) { - // issue#I7RJZT 运行时异常不做包装 - throw ExceptionUtil.wrapRuntime(e); - //throw new RuntimeException(e); - } + // issue#3686 由于这个方法内的加锁是get独立锁,不和put锁互斥,而put和pruneCache会修改cacheMap,导致在pruneCache过程中get会有并发问题 + // 因此此处需要使用带全局锁的get获取值 + v = get(key, isUpdateLastAccess); + if (null == v) { + // supplier的创建是一个耗时过程,此处创建与全局锁无关,而与key锁相关,这样就保证每个key只创建一个value,且互斥 + v = supplier.callWithRuntimeException(); put(key, v, timeout); - } else { - v = co.get(isUpdateLastAccess); } } finally { keyLock.unlock();