|
|
@@ -30,6 +30,9 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|
|
|
|
|
protected Map<K, CacheObj<K, V>> cacheMap;
|
|
|
|
|
|
+ // 乐观锁,此处使用乐观锁解决读多写少的场景
|
|
|
+ // get时乐观读,再检查是否修改,修改则转入悲观读重新读一遍,可以有效解决在写时阻塞大量读操作的情况。
|
|
|
+ // see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html
|
|
|
private final StampedLock lock = new StampedLock();
|
|
|
|
|
|
/**
|
|
|
@@ -52,11 +55,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|
|
protected boolean existCustomTimeout;
|
|
|
|
|
|
/**
|
|
|
- * 命中数
|
|
|
+ * 命中数,即命中缓存计数
|
|
|
*/
|
|
|
protected AtomicLong hitCount = 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) {
|
|
|
V v = get(key, isUpdateLastAccess);
|
|
|
if (null == v && null != supplier) {
|
|
|
- //每个key单独获取一把锁,降低锁的粒度提高并发能力
|
|
|
- Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
|
|
|
+ //每个key单独获取一把锁,降低锁的粒度提高并发能力,see pr#1385@Github
|
|
|
+ final Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
|
|
|
keyLock.lock();
|
|
|
try {
|
|
|
// 双重检查锁
|
|
|
@@ -157,7 +160,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|
|
}
|
|
|
put(key, v, this.timeout);
|
|
|
} else {
|
|
|
- v = co.get(true);
|
|
|
+ v = co.get(isUpdateLastAccess);
|
|
|
}
|
|
|
} finally {
|
|
|
keyLock.unlock();
|
|
|
@@ -170,25 +173,28 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
|
|
@Override
|
|
|
public V get(K key, boolean isUpdateLastAccess) {
|
|
|
// 尝试读取缓存,使用乐观读锁
|
|
|
- long stamp = lock.readLock();
|
|
|
- try {
|
|
|
- // 不存在或已移除
|
|
|
- final CacheObj<K, V> co = cacheMap.get(key);
|
|
|
- if (null == co) {
|
|
|
- missCount.getAndIncrement();
|
|
|
- return null;
|
|
|
+ long stamp = lock.tryOptimisticRead();
|
|
|
+ CacheObj<K, V> co = cacheMap.get(key);
|
|
|
+ if(false == lock.validate(stamp)){
|
|
|
+ // 有写线程修改了此对象,悲观读
|
|
|
+ stamp = lock.readLock();
|
|
|
+ try {
|
|
|
+ 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);
|
|
|
return null;
|
|
|
}
|