mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-04-19 03:01:48 +08:00
fix cache
This commit is contained in:
parent
02bd117fd2
commit
a8add399c2
@ -13,6 +13,7 @@
|
|||||||
* 【core 】 修改上传文件检查逻辑
|
* 【core 】 修改上传文件检查逻辑
|
||||||
* 【core 】 修正LocalDateTimeUtil.offset方法注释问题(issue#I2EEXC@Gitee)
|
* 【core 】 修正LocalDateTimeUtil.offset方法注释问题(issue#I2EEXC@Gitee)
|
||||||
* 【extra 】 VelocityEngine的getRowEngine改为getRawEngine(issue#I2EGRG@Gitee)
|
* 【extra 】 VelocityEngine的getRowEngine改为getRawEngine(issue#I2EGRG@Gitee)
|
||||||
|
* 【cache 】 缓存降低锁的粒度,提高并发能力(pr#1385@Github)
|
||||||
|
|
||||||
### Bug修复
|
### Bug修复
|
||||||
* 【core 】 修复FileUtil.move以及PathUtil.copy等无法自动创建父目录的问题(issue#I2CKTI@Gitee)
|
* 【core 】 修复FileUtil.move以及PathUtil.copy等无法自动创建父目录的问题(issue#I2CKTI@Gitee)
|
||||||
|
@ -83,7 +83,7 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
|
|||||||
* <p>
|
* <p>
|
||||||
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
|
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
|
||||||
* <p>
|
* <p>
|
||||||
* 每次调用此方法会刷新最后访问时间,也就是说会重新计算超时时间。
|
* 每次调用此方法会可选是否刷新最后访问时间,{@code true}表示会重新计算超时时间。
|
||||||
*
|
*
|
||||||
* @param key 键
|
* @param key 键
|
||||||
* @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。
|
* @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。
|
||||||
@ -96,6 +96,8 @@ public interface Cache<K, V> extends Iterable<V>, Serializable {
|
|||||||
* 从缓存中获得对象,当对象不在缓存中或已经过期返回{@code null}
|
* 从缓存中获得对象,当对象不在缓存中或已经过期返回{@code null}
|
||||||
* <p>
|
* <p>
|
||||||
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
|
* 调用此方法时,会检查上次调用时间,如果与当前时间差值大于超时时间返回{@code null},否则返回值。
|
||||||
|
* <p>
|
||||||
|
* 每次调用此方法会可选是否刷新最后访问时间,{@code true}表示会重新计算超时时间。
|
||||||
*
|
*
|
||||||
* @param key 键
|
* @param key 键
|
||||||
* @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。
|
* @param isUpdateLastAccess 是否更新最后访问时间,即重新计算超时时间。
|
||||||
|
@ -30,6 +30,9 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
|
|
||||||
protected Map<K, CacheObj<K, V>> cacheMap;
|
protected Map<K, CacheObj<K, V>> cacheMap;
|
||||||
|
|
||||||
|
// 乐观锁,此处使用乐观锁解决读多写少的场景
|
||||||
|
// get时乐观读,再检查是否修改,修改则转入悲观读重新读一遍,可以有效解决在写时阻塞大量读操作的情况。
|
||||||
|
// see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html
|
||||||
private final StampedLock lock = new StampedLock();
|
private final StampedLock lock = new StampedLock();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,11 +55,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
protected boolean existCustomTimeout;
|
protected boolean existCustomTimeout;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 命中数
|
* 命中数,即命中缓存计数
|
||||||
*/
|
*/
|
||||||
protected AtomicLong hitCount = new AtomicLong();
|
protected AtomicLong hitCount = new AtomicLong();
|
||||||
/**
|
/**
|
||||||
* 丢失数
|
* 丢失数,即未命中缓存计数
|
||||||
*/
|
*/
|
||||||
protected AtomicLong missCount = new AtomicLong();
|
protected AtomicLong missCount = new AtomicLong();
|
||||||
|
|
||||||
@ -143,8 +146,8 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) {
|
public V get(K key, boolean isUpdateLastAccess, Func0<V> supplier) {
|
||||||
V v = get(key, isUpdateLastAccess);
|
V v = get(key, isUpdateLastAccess);
|
||||||
if (null == v && null != supplier) {
|
if (null == v && null != supplier) {
|
||||||
//每个key单独获取一把锁,降低锁的粒度提高并发能力
|
//每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github
|
||||||
Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
|
final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
|
||||||
keyLock.lock();
|
keyLock.lock();
|
||||||
try {
|
try {
|
||||||
// 双重检查锁
|
// 双重检查锁
|
||||||
@ -157,7 +160,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
}
|
}
|
||||||
put(key, v, this.timeout);
|
put(key, v, this.timeout);
|
||||||
} else {
|
} else {
|
||||||
v = co.get(true);
|
v = co.get(isUpdateLastAccess);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
keyLock.unlock();
|
keyLock.unlock();
|
||||||
@ -170,25 +173,28 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|||||||
@Override
|
@Override
|
||||||
public V get(K key, boolean isUpdateLastAccess) {
|
public V get(K key, boolean isUpdateLastAccess) {
|
||||||
// 尝试读取缓存,使用乐观读锁
|
// 尝试读取缓存,使用乐观读锁
|
||||||
long stamp = lock.readLock();
|
long stamp = lock.tryOptimisticRead();
|
||||||
try {
|
CacheObj<K, V> co = cacheMap.get(key);
|
||||||
// 不存在或已移除
|
if(false == lock.validate(stamp)){
|
||||||
final CacheObj<K, V> co = cacheMap.get(key);
|
// 有写线程修改了此对象,悲观读
|
||||||
if (null == co) {
|
stamp = lock.readLock();
|
||||||
missCount.getAndIncrement();
|
try {
|
||||||
return null;
|
co = cacheMap.get(key);
|
||||||
|
} finally {
|
||||||
|
lock.unlockRead(stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 命中
|
|
||||||
if (false == co.isExpired()) {
|
|
||||||
hitCount.getAndIncrement();
|
|
||||||
return co.get(isUpdateLastAccess);
|
|
||||||
}
|
|
||||||
} finally {
|
|
||||||
lock.unlockRead(stamp);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 过期
|
// 未命中
|
||||||
|
if (null == co) {
|
||||||
|
missCount.getAndIncrement();
|
||||||
|
return null;
|
||||||
|
} else if (false == co.isExpired()) {
|
||||||
|
hitCount.getAndIncrement();
|
||||||
|
return co.get(isUpdateLastAccess);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 过期,既不算命中也不算非命中
|
||||||
remove(key, true);
|
remove(key, true);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ public class WeakCache<K, V> extends TimedCache<K, V>{
|
|||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
public WeakCache(long timeout) {
|
public WeakCache(long timeout) {
|
||||||
super(timeout, new WeakHashMap<K, CacheObj<K, V>>());
|
super(timeout, new WeakHashMap<>());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user