ReentrantCache增加setUseKeyLock让用户选择是否使用键锁来增强性能,或者保证安全。(issue#4022@Github)

This commit is contained in:
Looly 2025-08-25 09:09:37 +08:00
parent a0d946b3a8
commit 9f7dedea90
2 changed files with 47 additions and 1 deletions

View File

@ -2,7 +2,7 @@
# 🚀Changelog
-------------------------------------------------------------------------------------------------------------
# 5.8.40(2025-08-21)
# 5.8.40(2025-08-25)
### 🐣新特性
* 【captcha】 `MathGenerator`四则运算方式支持不生成负数结果pr#1363@Gitee
@ -21,6 +21,7 @@
* 【extra 】 修复`QLExpressEngine`allowClassSet无效问题issue#3994@Github
* 【core 】 修复`StrBuilder`insert插入计算错误问题issue#ICTSRZ@Gitee
* 【cron 】 修复`CronPatternUtil.nextDateAfter`计算下一个匹配表达式的日期时计算错误问题issue#4006@Github
* 【cache 】 `ReentrantCache`增加`setUseKeyLock`让用户选择是否使用键锁来增强性能或者保证安全。issue#4022@Github
-------------------------------------------------------------------------------------------------------------
# 5.8.39(2025-06-20)

View File

@ -1,6 +1,7 @@
package cn.hutool.cache.impl;
import cn.hutool.core.collection.CopiedIter;
import cn.hutool.core.lang.func.Func0;
import cn.hutool.core.lang.mutable.Mutable;
import java.util.HashSet;
@ -24,6 +25,20 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
// TODO 最优的解决方案是使用Guava的ConcurrentLinkedHashMap此处使用简化的互斥锁
protected final ReentrantLock lock = new ReentrantLock();
private boolean useKeyLock = true;
/**
* 设置是否使用key锁默认为true
*
* @param useKeyLock 是否使用key锁
* @return this
* @since 5.8.40
*/
public ReentrantCache<K, V> setUseKeyLock(boolean useKeyLock) {
this.useKeyLock = useKeyLock;
return this;
}
@Override
public void put(K key, V object, long timeout) {
lock.lock();
@ -44,6 +59,36 @@ public abstract class ReentrantCache<K, V> extends AbstractCache<K, V> {
return getOrRemoveExpired(key, isUpdateLastAccess, true);
}
@Override
public V get(final K key, final boolean isUpdateLastAccess, final long timeout, final Func0<V> valueFactory) {
if(useKeyLock){
// 用户如果允许则使用key锁可以减少valueFactory对象创建造成的
return super.get(key, isUpdateLastAccess, timeout, valueFactory);
}
V v = get(key, isUpdateLastAccess);
// 对象不存在则加锁创建
if (null == v && null != valueFactory) {
// 按照pr#1385提议使用key锁可以避免对象创建等待问题但是会带来循环锁问题issue#4022
// 因此此处依旧采用全局锁在对象创建过程中全局等待避免循环锁依赖
// 这样避免了循环锁但是会存在一个缺点即对象创建过程中其它线程无法获得锁从而无法使用缓存因此需要考虑对象创建的耗时问题
lock.lock();
try {
// 双重检查锁防止在竞争锁的过程中已经有其它线程写入
final CacheObj<K, V> co = getWithoutLock(key);
if (null == co) {
// supplier的创建是一个耗时过程此处创建与全局锁无关而与key锁相关这样就保证每个key只创建一个value且互斥
v = valueFactory.callWithRuntimeException();
put(key, v, timeout);
}
} finally {
lock.unlock();
}
}
return v;
}
@Override
public Iterator<CacheObj<K, V>> cacheObjIterator() {
CopiedIter<CacheObj<K, V>> copiedIterator;