修复AbstractCacheputWithoutLock方法可能导致的外部资源泄露问题(pr#3958@Github)

This commit is contained in:
Looly
2025-06-20 17:34:29 +08:00
parent 90b83d16a5
commit 6574dd010b
3 changed files with 32 additions and 2 deletions

View File

@@ -107,7 +107,9 @@ public abstract class AbstractCache<K, V> implements Cache<K, V> {
final MutableObj<K> mKey = MutableObj.of(key);
// issue#3618 对于替换的键值对,不做满队列检查和清除
if (cacheMap.containsKey(mKey)) {
final CacheObj<K, V> oldObj = cacheMap.get(mKey);
if (null != oldObj) {
onRemove(oldObj.key, oldObj.obj);
// 存在相同key覆盖之
cacheMap.put(mKey, co);
} else {

View File

@@ -17,8 +17,11 @@
package org.dromara.hutool.core.cache.impl;
import org.dromara.hutool.core.collection.iter.CopiedIter;
import org.dromara.hutool.core.collection.set.SetUtil;
import org.dromara.hutool.core.lang.mutable.Mutable;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -98,7 +101,14 @@ public abstract class LockedCache<K, V> extends AbstractCache<K, V> {
public void clear() {
lock.lock();
try {
cacheMap.clear();
// 获取所有键的副本
final Set<Mutable<K>> keys = SetUtil.of(cacheMap.keySet());
for (final Mutable<K> key : keys) {
final CacheObj<K, V> co = removeWithoutLock(key.get());
if (co != null) {
onRemove(co.key, co.obj); // 触发资源释放
}
}
} finally {
lock.unlock();
}

View File

@@ -164,4 +164,22 @@ public class CacheTest {
assertFalse(ALARM_CACHE.containsKey(1));
assertEquals(1, counter.get());
}
/**
* ReentrantCache类clear()方法、AbstractCache.putWithoutLock方法可能导致资源泄露
* https://github.com/chinabugotech/hutool/issues/3957
*/
@Test
public void reentrantCache_clear_Method_Test() {
final AtomicInteger removeCount = new AtomicInteger();
final Cache<String, String> lruCache = CacheUtil.newLRUCache(4);
lruCache.setListener((key, cachedObject) -> removeCount.getAndIncrement());
lruCache.put("key1","String1");
lruCache.put("key2","String2");
lruCache.put("key3","String3");
lruCache.put("key1","String4");//key已经存在原始putWithoutLock方法存在资源泄露
lruCache.put("key4","String5");
lruCache.clear();//ReentrantCache类clear()方法存在资源泄露
Assertions.assertEquals(5, removeCount.get());
}
}