remove key check

This commit is contained in:
Looly
2025-08-22 11:50:56 +08:00
parent 3d6c1e115e
commit a0d946b3a8
2 changed files with 2 additions and 24 deletions

View File

@@ -21,7 +21,6 @@
* 【extra 】 修复`QLExpressEngine`allowClassSet无效问题issue#3994@Github
* 【core 】 修复`StrBuilder`insert插入计算错误问题issue#ICTSRZ@Gitee
* 【cron 】 修复`CronPatternUtil.nextDateAfter`计算下一个匹配表达式的日期时计算错误问题issue#4006@Github
* 【cach 】 修复`AbstrachCache.get`可能造成的死锁问题issue#4022@Github
-------------------------------------------------------------------------------------------------------------
# 5.8.39(2025-06-20)

View File

@@ -7,7 +7,6 @@ import cn.hutool.core.lang.mutable.Mutable;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.map.SafeConcurrentHashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -66,11 +65,6 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
*/
protected CacheListener<K, V> listener;
/**
* 相同线程key缓存用于检查key循环引用导致的死锁
*/
private final ThreadLocal<Set<K>> loadingKeys = ThreadLocal.withInitial(HashSet::new);
// ---------------------------------------------------------------- put start
@Override
public void put(K key, V object) {
@@ -132,12 +126,6 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
public V get(K key, boolean isUpdateLastAccess, long timeout, Func0<V> supplier) {
V v = get(key, isUpdateLastAccess);
if (null == v && null != supplier) {
// 在尝试加锁前,检查当前线程是否已经在加载这个 keyissue#4022
// 如果是,则说明发生了循环依赖。
if (loadingKeys.get().contains(key)) {
throw new IllegalStateException("Circular dependency detected for key: " + key);
}
//每个key单独获取一把锁降低锁的粒度提高并发能力see pr#1385@Github
final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
keyLock.lock();
@@ -146,17 +134,8 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
// issue#3686 由于这个方法内的加锁是get独立锁不和put锁互斥而put和pruneCache会修改cacheMap导致在pruneCache过程中get会有并发问题
// 因此此处需要使用带全局锁的get获取值
v = get(key, isUpdateLastAccess);
if (null == v) {
loadingKeys.get().add(key);
// supplier的创建是一个耗时过程此处创建与全局锁无关而与key锁相关这样就保证每个key只创建一个value且互斥
try {
v = supplier.callWithRuntimeException();
put(key, v, timeout);
} finally {
// 无论 supplier 执行成功还是失败,都必须在 finally 块中移除标记
loadingKeys.get().remove(key);
}
}
v = supplier.callWithRuntimeException();
put(key, v, timeout);
} finally {
keyLock.unlock();
keyLockMap.remove(key);