Merge pull request #1385 from graydovee/v5-dev

缓存降低锁的粒度,提高并发能力
This commit is contained in:
Golden Looly 2021-01-24 21:17:46 +08:00 committed by GitHub
commit 02bd117fd2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
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.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong; import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock; 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(); private final StampedLock lock = new StampedLock();
/**
* 写的时候每个key一把锁降低锁的粒度
*/
protected final Map<K, Lock> keyLockMap = new ConcurrentHashMap<>();
/** /**
* 返回缓存容量{@code 0}表示无大小限制 * 返回缓存容量{@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) { 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) {
final long stamp = lock.writeLock(); //每个key单独获取一把锁降低锁的粒度提高并发能力
Lock keyLock = keyLockMap.computeIfAbsent(key, k -> new ReentrantLock());
keyLock.lock();
try { try {
// 双重检查锁 // 双重检查锁
final CacheObj<K, V> co = cacheMap.get(key); 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) { } catch (Exception e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
putWithoutLock(key, v, this.timeout); put(key, v, this.timeout);
} else { } else {
v = co.get(true); v = co.get(true);
} }
} finally { } finally {
lock.unlockWrite(stamp); keyLock.unlock();
keyLockMap.remove(key);
} }
} }
return v; return v;

View File

@ -3,14 +3,19 @@ package cn.hutool.cache.test;
import cn.hutool.cache.Cache; import cn.hutool.cache.Cache;
import cn.hutool.cache.impl.FIFOCache; import cn.hutool.cache.impl.FIFOCache;
import cn.hutool.cache.impl.LRUCache; import cn.hutool.cache.impl.LRUCache;
import cn.hutool.cache.impl.WeakCache;
import cn.hutool.core.lang.Console; import cn.hutool.core.lang.Console;
import cn.hutool.core.thread.ConcurrencyTester;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import org.junit.Assert;
import org.junit.Ignore; import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import java.util.concurrent.atomic.AtomicInteger;
/** /**
* 缓存单元测试 * 缓存单元测试
* *
* @author looly * @author looly
* *
*/ */
@ -46,7 +51,7 @@ public class CacheConcurrentTest {
System.out.println("=============================="); System.out.println("==============================");
ThreadUtil.sleep(10000); ThreadUtil.sleep(10000);
} }
@Test @Test
@Ignore @Ignore
public void lruCacheTest() { public void lruCacheTest() {
@ -72,7 +77,7 @@ public class CacheConcurrentTest {
} }
}); });
} }
ThreadUtil.sleep(5000); ThreadUtil.sleep(5000);
} }
@ -82,4 +87,22 @@ public class CacheConcurrentTest {
Console.log(tt); 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);
}
} }