修复AbstractCache.get中锁不一致导致的并发问题

This commit is contained in:
Looly 2024-08-08 15:47:06 +08:00
parent b6f1d59b5f
commit 31495c7157
2 changed files with 29 additions and 5 deletions

View File

@ -43,7 +43,7 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
/** /**
* 缓存Map * 缓存Map子类中初始化
*/ */
protected Map<Mutable<K>, CacheObj<K, V>> cacheMap; protected Map<Mutable<K>, CacheObj<K, V>> cacheMap;
@ -145,12 +145,13 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
keyLock.lock(); keyLock.lock();
try { try {
// 双重检查锁防止在竞争锁的过程中已经有其它线程写入 // 双重检查锁防止在竞争锁的过程中已经有其它线程写入
final CacheObj<K, V> co = getWithoutLock(key); // issue#3686 由于这个方法内的加锁是get独立锁不和put锁互斥而put和pruneCache会修改cacheMap导致在pruneCache过程中get会有并发问题
if (null == co || co.isExpired()) { // 因此此处需要使用带全局锁的get获取值
v = get(key, isUpdateLastAccess);
if (null == v) {
// supplier的创建是一个耗时过程此处创建与全局锁无关而与key锁相关这样就保证每个key只创建一个value且互斥
v = supplier.get(); v = supplier.get();
put(key, v, timeout); put(key, v, timeout);
} else {
v = co.get(isUpdateLastAccess);
} }
} finally { } finally {
keyLock.unlock(); keyLock.unlock();

View File

@ -0,0 +1,23 @@
package org.dromara.hutool.core.cache;
public class Issue3686Test {
public static void main(final String[] args) {
// final LRUCache<Long, Integer> objects = CacheUtil.newLRUCache(20, TimeUnit.SECONDS.toMillis(30));
// final List<Thread> list = new ArrayList<>();
// for (int i = 0; i < 10; i++) {
// final Thread thread = new Thread(() -> {
// while (true) {
// for (int i1 = 0; i1 < 100; i1++) {
// final int finalI = i1;
// objects.get((long) i1, () -> finalI);
// }
// ThreadUtil.sleep(500);
// }
// });
// list.add(thread);
// }
// for (final Thread thread : list) {
// thread.start();
// }
}
}