Files
hutool/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java

164 lines
3.7 KiB
Java
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package cn.hutool.cache.impl;
import cn.hutool.core.collection.CopiedIter;
import cn.hutool.core.thread.ThreadUtil;
import java.util.Iterator;
import java.util.concurrent.locks.StampedLock;
/**
* 使用{@link StampedLock}保护的缓存,使用读写乐观锁
*
* @param <K> 键类型
* @param <V> 值类型
* @author looly
* @since 5.7.15
*/
public abstract class StampedCache<K, V> extends AbstractCache<K, V> {
private static final long serialVersionUID = 1L;
// 乐观锁,此处使用乐观锁解决读多写少的场景
// get时乐观读再检查是否修改修改则转入悲观读重新读一遍可以有效解决在写时阻塞大量读操作的情况。
// see: https://www.cnblogs.com/jiagoushijuzi/p/13721319.html
protected final StampedLock lock = new StampedLock();
@Override
public void put(K key, V object, long timeout) {
final long stamp = lock.writeLock();
try {
putWithoutLock(key, object, timeout);
} finally {
lock.unlockWrite(stamp);
}
}
@Override
public boolean containsKey(K key) {
return null != get(key, false, false);
}
@Override
public V get(K key, boolean isUpdateLastAccess) {
return get(key, isUpdateLastAccess, true);
}
@Override
public Iterator<CacheObj<K, V>> cacheObjIterator() {
CopiedIter<CacheObj<K, V>> copiedIterator;
final long stamp = lock.readLock();
try {
copiedIterator = CopiedIter.copyOf(cacheObjIter());
} finally {
lock.unlockRead(stamp);
}
return new CacheObjIterator<>(copiedIterator);
}
@Override
public final int prune() {
final long stamp = lock.writeLock();
try {
return pruneCache();
} finally {
lock.unlockWrite(stamp);
}
}
@Override
public void remove(K key) {
final long stamp = lock.writeLock();
CacheObj<K, V> co;
try {
co = removeWithoutLock(key);
} finally {
lock.unlockWrite(stamp);
}
if (null != co) {
onRemove(co.key, co.obj);
}
}
@Override
public void clear() {
final long stamp = lock.writeLock();
try {
cacheMap.clear();
} finally {
lock.unlockWrite(stamp);
}
}
/**
* 获取值
*
* @param key 键
* @param isUpdateLastAccess 是否更新最后修改时间
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 值或null
*/
private V get(K key, boolean isUpdateLastAccess, boolean isUpdateCount) {
// 尝试读取缓存,使用乐观读锁
long stamp = lock.tryOptimisticRead();
CacheObj<K, V> co = getWithoutLock(key);
if (false == lock.validate(stamp)) {
// 有写线程修改了此对象,悲观读
stamp = lock.readLock();
try {
co = getWithoutLock(key);
} finally {
lock.unlockRead(stamp);
}
}
// 未命中
if (null == co) {
if (isUpdateCount) {
missCount.increment();
}
return null;
} else if (false == co.isExpired()) {
if (isUpdateCount) {
hitCount.increment();
}
return co.get(isUpdateLastAccess);
}
// 悲观锁,二次检查
return getOrRemoveExpired(key, isUpdateCount);
}
/**
* 同步获取值,如果过期则移除之
*
* @param key 键
* @param isUpdateCount 是否更新命中数get时更新contains时不更新
* @return 有效值或null
*/
private V getOrRemoveExpired(K key, boolean isUpdateCount) {
final long stamp = lock.writeLock();
CacheObj<K, V> co;
try {
co = getWithoutLock(key);
if (null == co) {
return null;
}
if (false == co.isExpired()) {
// 首先尝试获取值,如果值存在且有效,返回之
if (isUpdateCount) {
hitCount.increment();
}
return co.getValue();
}
// 无效移除
co = removeWithoutLock(key);
} finally {
lock.unlockWrite(stamp);
}
if (null != co) {
onRemove(co.key, co.obj);
}
return null;
}
}