mirror of
https://gitee.com/dromara/hutool.git
synced 2026-02-09 09:16:26 +08:00
remove key lock
This commit is contained in:
@@ -53,11 +53,6 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
*/
|
||||
protected Map<Mutable<K>, CacheObj<K, V>> cacheMap;
|
||||
|
||||
/**
|
||||
* 写的时候每个key一把锁,降低锁的粒度
|
||||
*/
|
||||
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* 返回缓存容量,{@code 0}表示无大小限制
|
||||
*/
|
||||
@@ -139,33 +134,8 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
return missCount.sum();
|
||||
}
|
||||
|
||||
@Override
|
||||
public V get(final K key, final boolean isUpdateLastAccess, final long timeout, final SerSupplier<V> valueFactory) {
|
||||
V v = get(key, isUpdateLastAccess);
|
||||
if (null == v && null != valueFactory) {
|
||||
//每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github
|
||||
final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
|
||||
keyLock.lock();
|
||||
try {
|
||||
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
|
||||
// issue#3686 由于这个方法内的加锁是get独立锁,不和put锁互斥,而put和pruneCache会修改cacheMap,导致在pruneCache过程中get会有并发问题
|
||||
// 因此此处需要使用带全局锁的get获取值
|
||||
v = get(key, isUpdateLastAccess);
|
||||
if (null == v) {
|
||||
// supplier的创建是一个耗时过程,此处创建与全局锁无关,而与key锁相关,这样就保证每个key只创建一个value,且互斥
|
||||
v = valueFactory.get();
|
||||
put(key, v, timeout);
|
||||
}
|
||||
} finally {
|
||||
keyLock.unlock();
|
||||
keyLockMap.remove(key);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取键对应的{@link CacheObj}
|
||||
* 获取键对应的{@link CacheObj},不判断是否过期
|
||||
*
|
||||
* @param key 键,实际使用时会被包装为{@link MutableObj}
|
||||
* @return {@link CacheObj}
|
||||
@@ -174,6 +144,23 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
protected CacheObj<K, V> getWithoutLock(final K key) {
|
||||
return this.cacheMap.get(MutableObj.of(key));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得CacheObj或清除过期CacheObj,不加锁
|
||||
*
|
||||
* @param key 键
|
||||
* @return 值或null
|
||||
*/
|
||||
protected CacheObj<K, V> getOrRemoveExpiredWithoutLock(final K key) {
|
||||
CacheObj<K, V> co = getWithoutLock(key);
|
||||
if (null != co && co.isExpired()) {
|
||||
//过期移除
|
||||
removeWithoutLock(key);
|
||||
onRemove(co.key, co.obj);
|
||||
co = null;
|
||||
}
|
||||
return co;
|
||||
}
|
||||
// ---------------------------------------------------------------- get end
|
||||
|
||||
@Override
|
||||
|
||||
@@ -18,6 +18,7 @@ package cn.hutool.v7.core.cache.impl;
|
||||
|
||||
import cn.hutool.v7.core.collection.iter.CopiedIter;
|
||||
import cn.hutool.v7.core.collection.set.SetUtil;
|
||||
import cn.hutool.v7.core.func.SerSupplier;
|
||||
import cn.hutool.v7.core.lang.mutable.Mutable;
|
||||
|
||||
import java.io.Serial;
|
||||
@@ -63,6 +64,31 @@ public abstract class LockedCache<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 SerSupplier<V> valueFactory) {
|
||||
V v = get(key, isUpdateLastAccess);
|
||||
|
||||
// 对象不存在,则加锁创建
|
||||
if (null == v && null != valueFactory) {
|
||||
// 按照pr#1385提议,使用key锁可以避免对象创建等待问题,但是会带来循环锁问题,见:issue#4022
|
||||
// 因此此处依旧采用全局锁,在对象创建过程中,全局等待,避免循环锁依赖
|
||||
// 这样避免了循环锁,但是会存在一个缺点,即对象创建过程中,其它线程无法获得锁,从而无法使用缓存,因此需要考虑对象创建的耗时问题
|
||||
lock.lock();
|
||||
try {
|
||||
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
|
||||
final CacheObj<K, V> co = getOrRemoveExpiredWithoutLock(key);
|
||||
if (null == co) {
|
||||
// supplier的创建是一个耗时过程,此处创建与全局锁无关,而与key锁相关,这样就保证每个key只创建一个value,且互斥
|
||||
v = valueFactory.get();
|
||||
put(key, v, timeout);
|
||||
}
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<CacheObj<K, V>> cacheObjIterator() {
|
||||
CopiedIter<CacheObj<K, V>> copiedIterator;
|
||||
@@ -129,35 +155,30 @@ public abstract class LockedCache<K, V> extends AbstractCache<K, V> {
|
||||
|
||||
/**
|
||||
* 获得值或清除过期值
|
||||
* @param key 键
|
||||
*
|
||||
* @param key 键
|
||||
* @param isUpdateLastAccess 是否更新最后访问时间
|
||||
* @param isUpdateCount 是否更新计数器
|
||||
* @param isUpdateCount 是否更新计数器
|
||||
* @return 值或null
|
||||
*/
|
||||
private V getOrRemoveExpired(final K key, final boolean isUpdateLastAccess, final boolean isUpdateCount) {
|
||||
CacheObj<K, V> co;
|
||||
lock.lock();
|
||||
try {
|
||||
co = getWithoutLock(key);
|
||||
if(null != co && co.isExpired()){
|
||||
//过期移除
|
||||
removeWithoutLock(key);
|
||||
onRemove(co.key, co.obj);
|
||||
co = null;
|
||||
}
|
||||
co = getOrRemoveExpiredWithoutLock(key);
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
|
||||
// 未命中
|
||||
if (null == co) {
|
||||
if(isUpdateCount){
|
||||
if (isUpdateCount) {
|
||||
missCount.increment();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if(isUpdateCount){
|
||||
if (isUpdateCount) {
|
||||
hitCount.increment();
|
||||
}
|
||||
return co.get(isUpdateLastAccess);
|
||||
|
||||
Reference in New Issue
Block a user