mirror of
https://gitee.com/chinabugotech/hutool.git
synced 2025-05-09 23:51:34 +08:00
commit
02bd117fd2
@ -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;
|
||||||
|
@ -3,11 +3,16 @@ 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;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 缓存单元测试
|
* 缓存单元测试
|
||||||
*
|
*
|
||||||
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user