mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-07-21 15:09:48 +08:00
add LockUtil and fix Cache
This commit is contained in:
@@ -6,9 +6,7 @@ import cn.hutool.core.lang.func.Func0;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
import java.util.concurrent.locks.StampedLock;
|
||||
|
||||
/**
|
||||
* 超时和限制大小的缓存的默认实现<br>
|
||||
@@ -17,32 +15,39 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
* <li>创建一个新的Map</li>
|
||||
* <li>实现 <code>prune</code> 策略</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Looly,jodd
|
||||
*
|
||||
* @param <K> 键类型
|
||||
* @param <V> 值类型
|
||||
* @author Looly, jodd
|
||||
*/
|
||||
public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
protected Map<K, CacheObj<K, V>> cacheMap;
|
||||
|
||||
private final ReentrantReadWriteLock cacheLock = new ReentrantReadWriteLock();
|
||||
private final ReadLock readLock = cacheLock.readLock();
|
||||
private final WriteLock writeLock = cacheLock.writeLock();
|
||||
private final StampedLock lock = new StampedLock();
|
||||
|
||||
/** 返回缓存容量,<code>0</code>表示无大小限制 */
|
||||
/**
|
||||
* 返回缓存容量,<code>0</code>表示无大小限制
|
||||
*/
|
||||
protected int capacity;
|
||||
/** 缓存失效时长, <code>0</code> 表示无限制,单位毫秒 */
|
||||
/**
|
||||
* 缓存失效时长, <code>0</code> 表示无限制,单位毫秒
|
||||
*/
|
||||
protected long timeout;
|
||||
|
||||
/** 每个对象是否有单独的失效时长,用于决定清理过期对象是否有必要。 */
|
||||
/**
|
||||
* 每个对象是否有单独的失效时长,用于决定清理过期对象是否有必要。
|
||||
*/
|
||||
protected boolean existCustomTimeout;
|
||||
|
||||
/** 命中数 */
|
||||
/**
|
||||
* 命中数
|
||||
*/
|
||||
protected int hitCount;
|
||||
/** 丢失数 */
|
||||
/**
|
||||
* 丢失数
|
||||
*/
|
||||
protected int missCount;
|
||||
|
||||
// ---------------------------------------------------------------- put start
|
||||
@@ -53,20 +58,19 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
@Override
|
||||
public void put(K key, V object, long timeout) {
|
||||
writeLock.lock();
|
||||
|
||||
final long stamp = lock.writeLock();
|
||||
try {
|
||||
putWithoutLock(key, object, timeout);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入元素,无锁
|
||||
*
|
||||
* @param key 键
|
||||
* @param object 值
|
||||
*
|
||||
* @param key 键
|
||||
* @param object 值
|
||||
* @param timeout 超时时长
|
||||
* @since 4.5.16
|
||||
*/
|
||||
@@ -85,8 +89,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
// ---------------------------------------------------------------- get start
|
||||
@Override
|
||||
public boolean containsKey(K key) {
|
||||
readLock.lock();
|
||||
|
||||
final long stamp = lock.readLock();
|
||||
try {
|
||||
// 不存在或已移除
|
||||
final CacheObj<K, V> co = cacheMap.get(key);
|
||||
@@ -99,7 +102,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
lock.unlockRead(stamp);
|
||||
}
|
||||
|
||||
// 过期
|
||||
@@ -111,24 +114,14 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
* @return 命中数
|
||||
*/
|
||||
public int getHitCount() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return hitCount;
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return hitCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 丢失数
|
||||
*/
|
||||
public int getMissCount() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return missCount;
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return missCount;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -140,11 +133,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
public V get(K key, Func0<V> supplier) {
|
||||
V v = get(key);
|
||||
if (null == v && null != supplier) {
|
||||
writeLock.lock();
|
||||
final long stamp = lock.writeLock();
|
||||
try {
|
||||
// 双重检查锁
|
||||
final CacheObj<K, V> co = cacheMap.get(key);
|
||||
if(null == co || co.isExpired() || null == co.getValue()) {
|
||||
if (null == co || co.isExpired()) {
|
||||
try {
|
||||
v = supplier.call();
|
||||
} catch (Exception e) {
|
||||
@@ -155,7 +148,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
v = co.get(true);
|
||||
}
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
return v;
|
||||
@@ -163,23 +156,25 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
@Override
|
||||
public V get(K key, boolean isUpdateLastAccess) {
|
||||
readLock.lock();
|
||||
|
||||
// 尝试读取缓存,使用乐观读锁
|
||||
long stamp = lock.readLock();
|
||||
try {
|
||||
// 不存在或已移除
|
||||
final CacheObj<K, V> co = cacheMap.get(key);
|
||||
if (co == null) {
|
||||
if (null == co) {
|
||||
missCount++;
|
||||
return null;
|
||||
}
|
||||
|
||||
if (false == co.isExpired()) {
|
||||
if (co.isExpired()) {
|
||||
missCount++;
|
||||
} else{
|
||||
// 命中
|
||||
hitCount++;
|
||||
return co.get(isUpdateLastAccess);
|
||||
}
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
lock.unlock(stamp);
|
||||
}
|
||||
|
||||
// 过期
|
||||
@@ -199,30 +194,32 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
@Override
|
||||
public Iterator<CacheObj<K, V>> cacheObjIterator() {
|
||||
CopiedIter<CacheObj<K, V>> copiedIterator;
|
||||
readLock.lock();
|
||||
final long stamp = lock.readLock();
|
||||
try {
|
||||
copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
|
||||
} finally {
|
||||
readLock.unlock();
|
||||
lock.unlockRead(stamp);
|
||||
}
|
||||
return new CacheObjIterator<>(copiedIterator);
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- prune start
|
||||
|
||||
/**
|
||||
* 清理实现
|
||||
*
|
||||
* 清理实现<br>
|
||||
* 子类实现此方法时无需加锁
|
||||
*
|
||||
* @return 清理数
|
||||
*/
|
||||
protected abstract int pruneCache();
|
||||
|
||||
@Override
|
||||
public final int prune() {
|
||||
writeLock.lock();
|
||||
final long stamp = lock.writeLock();
|
||||
try {
|
||||
return pruneCache();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
// ---------------------------------------------------------------- prune end
|
||||
@@ -235,7 +232,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
/**
|
||||
* @return 默认缓存失效时长。<br>
|
||||
* 每个对象可以单独设置失效时长
|
||||
* 每个对象可以单独设置失效时长
|
||||
*/
|
||||
@Override
|
||||
public long timeout() {
|
||||
@@ -244,26 +241,16 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
/**
|
||||
* 只有设置公共缓存失效时长或每个对象单独的失效时长时清理可用
|
||||
*
|
||||
*
|
||||
* @return 过期对象清理是否可用,内部使用
|
||||
*/
|
||||
protected boolean isPruneExpiredActive() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return (timeout != 0) || existCustomTimeout;
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return (timeout != 0) || existCustomTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFull() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return (capacity > 0) && (cacheMap.size() >= capacity);
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return (capacity > 0) && (cacheMap.size() >= capacity);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -273,49 +260,34 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
@Override
|
||||
public void clear() {
|
||||
writeLock.lock();
|
||||
final long stamp = lock.writeLock();
|
||||
try {
|
||||
cacheMap.clear();
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int size() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return cacheMap.size();
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return cacheMap.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return cacheMap.isEmpty();
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return cacheMap.isEmpty();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
this.readLock.lock();
|
||||
try {
|
||||
return this.cacheMap.toString();
|
||||
} finally {
|
||||
this.readLock.unlock();
|
||||
}
|
||||
return this.cacheMap.toString();
|
||||
}
|
||||
// ---------------------------------------------------------------- common end
|
||||
|
||||
/**
|
||||
* 对象移除回调。默认无动作
|
||||
*
|
||||
* @param key 键
|
||||
*
|
||||
* @param key 键
|
||||
* @param cachedObject 被缓存的对象
|
||||
*/
|
||||
protected void onRemove(K key, V cachedObject) {
|
||||
@@ -324,27 +296,27 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
|
||||
|
||||
/**
|
||||
* 移除key对应的对象
|
||||
*
|
||||
* @param key 键
|
||||
*
|
||||
* @param key 键
|
||||
* @param withMissCount 是否计数丢失数
|
||||
*/
|
||||
private void remove(K key, boolean withMissCount) {
|
||||
writeLock.lock();
|
||||
final long stamp = lock.writeLock();
|
||||
CacheObj<K, V> co;
|
||||
try {
|
||||
co = removeWithoutLock(key, withMissCount);
|
||||
} finally {
|
||||
writeLock.unlock();
|
||||
lock.unlockWrite(stamp);
|
||||
}
|
||||
if (null != co) {
|
||||
onRemove(co.key, co.obj);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 移除key对应的对象,不加锁
|
||||
*
|
||||
* @param key 键
|
||||
*
|
||||
* @param key 键
|
||||
* @param withMissCount 是否计数丢失数
|
||||
* @return 移除的对象,无返回null
|
||||
*/
|
||||
|
@@ -1,15 +1,12 @@
|
||||
package cn.hutool.cache.test;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
import cn.hutool.cache.Cache;
|
||||
import cn.hutool.cache.impl.FIFOCache;
|
||||
import cn.hutool.cache.impl.LRUCache;
|
||||
import cn.hutool.core.lang.Console;
|
||||
import cn.hutool.core.thread.ThreadUtil;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
/**
|
||||
* 缓存单元测试
|
||||
@@ -28,30 +25,22 @@ public class CacheConcurrentTest {
|
||||
// 由于缓存容量只有3,当加入第四个元素的时候,根据FIFO规则,最先放入的对象将被移除
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadUtil.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cache.put("key1", "value1", System.currentTimeMillis() * 3);
|
||||
cache.put("key2", "value2", System.currentTimeMillis() * 3);
|
||||
cache.put("key3", "value3", System.currentTimeMillis() * 3);
|
||||
cache.put("key4", "value4", System.currentTimeMillis() * 3);
|
||||
ThreadUtil.sleep(1000);
|
||||
cache.put("key5", "value5", System.currentTimeMillis() * 3);
|
||||
cache.put("key6", "value6", System.currentTimeMillis() * 3);
|
||||
cache.put("key7", "value7", System.currentTimeMillis() * 3);
|
||||
cache.put("key8", "value8", System.currentTimeMillis() * 3);
|
||||
Console.log("put all");
|
||||
}
|
||||
ThreadUtil.execute(() -> {
|
||||
cache.put("key1", "value1", System.currentTimeMillis() * 3);
|
||||
cache.put("key2", "value2", System.currentTimeMillis() * 3);
|
||||
cache.put("key3", "value3", System.currentTimeMillis() * 3);
|
||||
cache.put("key4", "value4", System.currentTimeMillis() * 3);
|
||||
ThreadUtil.sleep(1000);
|
||||
cache.put("key5", "value5", System.currentTimeMillis() * 3);
|
||||
cache.put("key6", "value6", System.currentTimeMillis() * 3);
|
||||
cache.put("key7", "value7", System.currentTimeMillis() * 3);
|
||||
cache.put("key8", "value8", System.currentTimeMillis() * 3);
|
||||
Console.log("put all");
|
||||
});
|
||||
}
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
ThreadUtil.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
show(cache);
|
||||
}
|
||||
});
|
||||
ThreadUtil.execute(() -> show(cache));
|
||||
}
|
||||
|
||||
System.out.println("==============================");
|
||||
@@ -66,23 +55,20 @@ public class CacheConcurrentTest {
|
||||
|
||||
for (int i = 0; i < threadCount; i++) {
|
||||
final int index = i;
|
||||
ThreadUtil.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
cache.put("key1"+ index, "value1");
|
||||
cache.put("key2"+ index, "value2", System.currentTimeMillis() * 3);
|
||||
|
||||
int size = cache.size();
|
||||
int capacity = cache.capacity();
|
||||
if(size > capacity) {
|
||||
Console.log("{} {}", size, capacity);
|
||||
}
|
||||
ThreadUtil.sleep(1000);
|
||||
size = cache.size();
|
||||
capacity = cache.capacity();
|
||||
if(size > capacity) {
|
||||
Console.log("## {} {}", size, capacity);
|
||||
}
|
||||
ThreadUtil.execute(() -> {
|
||||
cache.put("key1"+ index, "value1");
|
||||
cache.put("key2"+ index, "value2", System.currentTimeMillis() * 3);
|
||||
|
||||
int size = cache.size();
|
||||
int capacity = cache.capacity();
|
||||
if(size > capacity) {
|
||||
Console.log("{} {}", size, capacity);
|
||||
}
|
||||
ThreadUtil.sleep(1000);
|
||||
size = cache.size();
|
||||
capacity = cache.capacity();
|
||||
if(size > capacity) {
|
||||
Console.log("## {} {}", size, capacity);
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -91,10 +77,8 @@ public class CacheConcurrentTest {
|
||||
}
|
||||
|
||||
private void show(Cache<String, String> cache) {
|
||||
Iterator<?> its = cache.iterator();
|
||||
|
||||
while (its.hasNext()) {
|
||||
Object tt = its.next();
|
||||
for (Object tt : cache) {
|
||||
Console.log(tt);
|
||||
}
|
||||
}
|
||||
|
@@ -58,7 +58,7 @@ public class CacheTest {
|
||||
//使用时间推近
|
||||
lruCache.get("key1");
|
||||
lruCache.put("key4", "value4", DateUnit.SECOND.getMillis() * 3);
|
||||
|
||||
|
||||
String value1 = lruCache.get("key1");
|
||||
Assert.assertNotNull(value1);
|
||||
//由于缓存容量只有3,当加入第四个元素的时候,根据LRU规则,最少使用的将被移除(2被移除)
|
||||
|
Reference in New Issue
Block a user