optmize cache

This commit is contained in:
DuJiaHui
2021-01-22 21:09:46 +08:00
parent b7ca34d0e8
commit de7575c63f
2 changed files with 40 additions and 6 deletions

View File

@@ -7,7 +7,10 @@ import cn.hutool.core.lang.func.Func0;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
/**
@@ -29,6 +32,11 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
private final StampedLock lock = new StampedLock();
/**
* 写的时候每个key一把锁降低锁的粒度
*/
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
/**
* 返回缓存容量,{@code 0}表示无大小限制
*/
@@ -135,7 +143,9 @@ 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) {
final long stamp = lock.writeLock();
//每个key单独获取一把锁降低锁的粒度提高并发能力
Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
keyLock.lock();
try {
// 双重检查锁
final CacheObj<K, V> co = cacheMap.get(key);
@@ -145,12 +155,13 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
} catch (Exception e) {
throw new RuntimeException(e);
}
putWithoutLock(key, v, this.timeout);
put(key, v, this.timeout);
} else {
v = co.get(true);
}
} finally {
lock.unlockWrite(stamp);
keyLock.unlock();
keyLockMap.remove(key);
}
}
return v;

View File

@@ -3,11 +3,16 @@ package cn.hutool.cache.test;
import cn.hutool.cache.Cache;
import cn.hutool.cache.impl.FIFOCache;
import cn.hutool.cache.impl.LRUCache;
import cn.hutool.cache.impl.WeakCache;
import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ConcurrencyTester;
import cn.hutool.core.thread.ThreadUtil;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 缓存单元测试
*
@@ -82,4 +87,22 @@ public class CacheConcurrentTest {
Console.log(tt);
}
}
@Test
public void effectiveTest() {
// 模拟耗时操作消耗时间
int delay = 2000;
AtomicInteger ai = new AtomicInteger(0);
WeakCache<Integer, Integer> weakCache = new WeakCache<>(60 * 1000);
ConcurrencyTester concurrencyTester = ThreadUtil.concurrencyTest(32, () -> {
int i = ai.incrementAndGet() % 4;
weakCache.get(i, () -> {
ThreadUtil.sleep(delay);
return i;
});
});
long interval = concurrencyTester.getInterval();
// 总耗时应与单词操作耗时在同一个数量级
Assert.assertTrue(interval < delay * 2);
}
}