diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java
index 6bb549689..a52ac4713 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/AbstractCache.java
@@ -3,6 +3,8 @@ package cn.hutool.cache.impl;
import cn.hutool.cache.Cache;
import cn.hutool.cache.CacheListener;
import cn.hutool.core.lang.func.Func0;
+import cn.hutool.core.lang.mutable.Mutable;
+import cn.hutool.core.lang.mutable.MutableObj;
import java.util.Iterator;
import java.util.Map;
@@ -11,6 +13,7 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.LongAdder;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
+import java.util.stream.Collectors;
/**
* 超时和限制大小的缓存的默认实现
@@ -27,7 +30,7 @@ import java.util.concurrent.locks.ReentrantLock;
public abstract class AbstractCache implements Cache {
private static final long serialVersionUID = 1L;
- protected Map> cacheMap;
+ protected Map, CacheObj> cacheMap;
/**
* 写的时候每个key一把锁,降低锁的粒度
@@ -84,7 +87,7 @@ public abstract class AbstractCache implements Cache {
if (isFull()) {
pruneCache();
}
- cacheMap.put(key, co);
+ cacheMap.put(MutableObj.of(key), co);
}
// ---------------------------------------------------------------- put end
@@ -112,7 +115,7 @@ public abstract class AbstractCache implements Cache {
keyLock.lock();
try {
// 双重检查锁,防止在竞争锁的过程中已经有其它线程写入
- final CacheObj co = cacheMap.get(key);
+ final CacheObj co = getWithoutLock(key);
if (null == co || co.isExpired()) {
try {
v = supplier.call();
@@ -130,6 +133,16 @@ public abstract class AbstractCache implements Cache {
}
return v;
}
+
+ /**
+ * 获取键对应的{@link CacheObj}
+ * @param key 键,实际使用时会被包装为{@link MutableObj}
+ * @return {@link CacheObj}
+ * @since 5.8.0
+ */
+ protected CacheObj getWithoutLock(K key){
+ return this.cacheMap.get(MutableObj.of(key));
+ }
// ---------------------------------------------------------------- get end
@Override
@@ -212,7 +225,7 @@ public abstract class AbstractCache implements Cache {
* @since 5.5.9
*/
public Set keySet(){
- return this.cacheMap.keySet();
+ return this.cacheMap.keySet().stream().map(Mutable::get).collect(Collectors.toSet());
}
/**
@@ -237,11 +250,20 @@ public abstract class AbstractCache implements Cache {
* @return 移除的对象,无返回null
*/
protected CacheObj removeWithoutLock(K key, boolean withMissCount) {
- final CacheObj co = cacheMap.remove(key);
+ final CacheObj co = cacheMap.remove(MutableObj.of(key));
if (withMissCount) {
// 在丢失计数有效的情况下,移除一般为get时的超时操作,此处应该丢失数+1
this.missCount.increment();
}
return co;
}
+
+ /**
+ * 获取所有{@link CacheObj}值的{@link Iterator}形式
+ * @return {@link Iterator}
+ * @since 5.8.0
+ */
+ protected Iterator> cacheObjIter(){
+ return this.cacheMap.values().iterator();
+ }
}
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
index 558fed75f..a3fc35085 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/FIFOCache.java
@@ -50,7 +50,7 @@ public class FIFOCache extends StampedCache {
CacheObj first = null;
// 清理过期对象并找出链表头部元素(先入元素)
- Iterator> values = cacheMap.values().iterator();
+ final Iterator> values = cacheObjIter();
if (isPruneExpiredActive()) {
// 清理过期对象并找出链表头部元素(先入元素)
while (values.hasNext()) {
@@ -71,7 +71,7 @@ public class FIFOCache extends StampedCache {
// 清理结束后依旧是满的,则删除第一个被缓存的对象
if (isFull() && null != first) {
- cacheMap.remove(first.key);
+ removeWithoutLock(first.key, false);
onRemove(first.key, first.obj);
count++;
}
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
index 4b1e2608a..b3bcdffc9 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/LFUCache.java
@@ -57,7 +57,7 @@ public class LFUCache extends StampedCache {
CacheObj comin = null;
// 清理过期对象并找出访问最少的对象
- Iterator> values = cacheMap.values().iterator();
+ Iterator> values = cacheObjIter();
CacheObj co;
while (values.hasNext()) {
co = values.next();
@@ -78,7 +78,7 @@ public class LFUCache extends StampedCache {
if (isFull() && comin != null) {
long minAccessCount = comin.accessCount.get();
- values = cacheMap.values().iterator();
+ values = cacheObjIter();
CacheObj co1;
while (values.hasNext()) {
co1 = values.next();
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
index 29f83f02f..c95b97f2e 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/LRUCache.java
@@ -56,7 +56,7 @@ public class LRUCache extends ReentrantCache {
return 0;
}
int count = 0;
- Iterator> values = cacheMap.values().iterator();
+ Iterator> values = cacheObjIter();
CacheObj co;
while (values.hasNext()) {
co = values.next();
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
index a985ad70b..e45474040 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/ReentrantCache.java
@@ -36,7 +36,7 @@ public abstract class ReentrantCache extends AbstractCache {
lock.lock();
try {
// 不存在或已移除
- final CacheObj co = cacheMap.get(key);
+ final CacheObj co = getWithoutLock(key);
if (co == null) {
return false;
}
@@ -59,7 +59,7 @@ public abstract class ReentrantCache extends AbstractCache {
CacheObj co;
lock.lock();
try {
- co = cacheMap.get(key);
+ co = getWithoutLock(key);
} finally {
lock.unlock();
}
@@ -83,7 +83,7 @@ public abstract class ReentrantCache extends AbstractCache {
CopiedIter> copiedIterator;
lock.lock();
try {
- copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
+ copiedIterator = CopiedIter.copyOf(cacheObjIter());
} finally {
lock.unlock();
}
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java
index 79534f2bf..deaec6641 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/StampedCache.java
@@ -36,7 +36,7 @@ public abstract class StampedCache extends AbstractCache{
final long stamp = lock.readLock();
try {
// 不存在或已移除
- final CacheObj co = cacheMap.get(key);
+ final CacheObj co = getWithoutLock(key);
if (co == null) {
return false;
}
@@ -58,12 +58,12 @@ public abstract class StampedCache extends AbstractCache{
public V get(K key, boolean isUpdateLastAccess) {
// 尝试读取缓存,使用乐观读锁
long stamp = lock.tryOptimisticRead();
- CacheObj co = cacheMap.get(key);
+ CacheObj co = getWithoutLock(key);
if(false == lock.validate(stamp)){
// 有写线程修改了此对象,悲观读
stamp = lock.readLock();
try {
- co = cacheMap.get(key);
+ co = getWithoutLock(key);
} finally {
lock.unlockRead(stamp);
}
@@ -88,7 +88,7 @@ public abstract class StampedCache extends AbstractCache{
CopiedIter> copiedIterator;
final long stamp = lock.readLock();
try {
- copiedIterator = CopiedIter.copyOf(this.cacheMap.values().iterator());
+ copiedIterator = CopiedIter.copyOf(cacheObjIter());
} finally {
lock.unlockRead(stamp);
}
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
index e03ede728..dfccd942e 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/TimedCache.java
@@ -1,6 +1,7 @@
package cn.hutool.cache.impl;
import cn.hutool.cache.GlobalPruneTimer;
+import cn.hutool.core.lang.mutable.Mutable;
import java.util.HashMap;
import java.util.Iterator;
@@ -37,7 +38,7 @@ public class TimedCache extends StampedCache {
* @param timeout 过期时长
* @param map 存储缓存对象的map
*/
- public TimedCache(long timeout, Map> map) {
+ public TimedCache(long timeout, Map, CacheObj> map) {
this.capacity = 0;
this.timeout = timeout;
this.cacheMap = map;
@@ -52,7 +53,7 @@ public class TimedCache extends StampedCache {
@Override
protected int pruneCache() {
int count = 0;
- Iterator> values = cacheMap.values().iterator();
+ final Iterator> values = cacheObjIter();
CacheObj co;
while (values.hasNext()) {
co = values.next();
diff --git a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
index 21b2bb16a..90df20303 100644
--- a/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
+++ b/hutool-cache/src/main/java/cn/hutool/cache/impl/WeakCache.java
@@ -1,10 +1,5 @@
package cn.hutool.cache.impl;
-import cn.hutool.cache.Cache;
-import cn.hutool.core.lang.func.Func0;
-import cn.hutool.core.lang.mutable.MutableObj;
-
-import java.util.Iterator;
import java.util.WeakHashMap;
/**
@@ -12,112 +7,21 @@ import java.util.WeakHashMap;
* 对于一个给定的键,其映射的存在并不阻止垃圾回收器对该键的丢弃,这就使该键成为可终止的,被终止,然后被回收。
* 丢弃某个键时,其条目从映射中有效地移除。
*
- * @param 键类型
- * @param 值类型
* @author Looly
+ *
+ * @param 键
+ * @param 值
+ * @author looly
* @since 3.0.7
*/
-public class WeakCache implements Cache {
+public class WeakCache extends TimedCache{
private static final long serialVersionUID = 1L;
- TimedCache, V> timedCache;
-
/**
* 构造
- *
- * @param timeout 超时
+ * @param timeout 超时时常,单位毫秒,-1或0表示无限制
*/
public WeakCache(long timeout) {
- this.timedCache = new TimedCache<>(timeout, new WeakHashMap<>());
- }
-
- @Override
- public int capacity() {
- return timedCache.capacity();
- }
-
- @Override
- public long timeout() {
- return timedCache.timeout();
- }
-
- @Override
- public void put(K key, V object) {
- timedCache.put(new MutableObj<>(key), object);
- }
-
- @Override
- public void put(K key, V object, long timeout) {
- timedCache.put(new MutableObj<>(key), object, timeout);
- }
-
- @Override
- public V get(K key, boolean isUpdateLastAccess, Func0 supplier) {
- return timedCache.get(new MutableObj<>(key), isUpdateLastAccess, supplier);
- }
-
- @Override
- public V get(K key, boolean isUpdateLastAccess) {
- return timedCache.get(new MutableObj<>(key), isUpdateLastAccess);
- }
-
- @Override
- public Iterator> cacheObjIterator() {
- final Iterator, V>> timedIter = timedCache.cacheObjIterator();
- return new Iterator>() {
- @Override
- public boolean hasNext() {
- return timedIter.hasNext();
- }
-
- @Override
- public CacheObj next() {
- final CacheObj, V> next = timedIter.next();
- final CacheObj nextNew = new CacheObj<>(next.key.get(), next.obj, next.ttl);
- nextNew.lastAccess = next.lastAccess;
- nextNew.accessCount = next.accessCount;
- return nextNew;
- }
- };
- }
-
- @Override
- public int prune() {
- return timedCache.prune();
- }
-
- @Override
- public boolean isFull() {
- return timedCache.isFull();
- }
-
- @Override
- public void remove(K key) {
- timedCache.remove(new MutableObj<>(key));
- }
-
- @Override
- public void clear() {
- timedCache.clear();
- }
-
- @Override
- public int size() {
- return timedCache.size();
- }
-
- @Override
- public boolean isEmpty() {
- return timedCache.isEmpty();
- }
-
- @Override
- public boolean containsKey(K key) {
- return timedCache.containsKey(new MutableObj<>(key));
- }
-
- @Override
- public Iterator iterator() {
- return timedCache.iterator();
+ super(timeout, new WeakHashMap<>());
}
}
diff --git a/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java
index ce7b03dd8..19eb4f096 100644
--- a/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java
+++ b/hutool-cache/src/test/java/cn/hutool/cache/WeakCacheTest.java
@@ -1,6 +1,7 @@
package cn.hutool.cache;
import cn.hutool.cache.impl.WeakCache;
+import cn.hutool.core.lang.Console;
import org.junit.Assert;
import org.junit.Test;
@@ -14,8 +15,31 @@ public class WeakCacheTest {
Assert.assertEquals(2, cache.size());
+ // 检查被MutableObj包装的key能否正常移除
cache.remove("abc");
Assert.assertEquals(1, cache.size());
}
+
+ @Test
+ public void removeByGcTest(){
+ WeakCache cache = new WeakCache<>(-1);
+ cache.put("a", "1");
+ cache.put("b", "2");
+ Assert.assertEquals(2, cache.size());
+
+ // GC测试
+ int i=0;
+ while(true){
+ if(2 == cache.size()){
+ i++;
+ Console.log("Object is alive for {} loops - ", i);
+ System.gc();
+ }else{
+ Console.log("Object has been collected.");
+ Console.log(cache.size());
+ break;
+ }
+ }
+ }
}
diff --git a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java
index 72a490366..4b772c26e 100644
--- a/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java
+++ b/hutool-core/src/main/java/cn/hutool/core/lang/mutable/MutableObj.java
@@ -11,6 +11,17 @@ import java.io.Serializable;
public class MutableObj implements Mutable, Serializable {
private static final long serialVersionUID = 1L;
+ /**
+ * 构建MutableObj
+ * @param value 被包装的值
+ * @param 值类型
+ * @return MutableObj
+ * @since 5.8.0
+ */
+ public static MutableObj of(T value){
+ return new MutableObj<>(value);
+ }
+
private T value;
/**
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReferenceUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReferenceUtilTest.java
index 97fa55953..c1aed085d 100755
--- a/hutool-core/src/test/java/cn/hutool/core/util/ReferenceUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/ReferenceUtilTest.java
@@ -1,6 +1,9 @@
package cn.hutool.core.util;
+import cn.hutool.core.lang.Console;
+import cn.hutool.core.lang.mutable.MutableObj;
import org.junit.Assert;
+import org.junit.Ignore;
import org.junit.Test;
import java.lang.ref.PhantomReference;
@@ -31,4 +34,23 @@ public class ReferenceUtilTest {
// get方法永远都返回null,PhantomReference只能用来监控对象的GC状况
Assert.assertNull(integerReference.get());
}
+
+ @Test
+ @Ignore
+ public void gcTest(){
+ // https://blog.csdn.net/zmx729618/article/details/54093532
+ // 弱引用的对象必须使用可变对象,不能使用常量对象(比如String)
+ WeakReference> reference = new WeakReference<>(new MutableObj<>("abc"));
+ int i=0;
+ while(true){
+ if(reference.get()!=null){
+ i++;
+ Console.log("Object is alive for {} loops - ", i);
+ System.gc();
+ }else{
+ Console.log("Object has been collected.");
+ break;
+ }
+ }
+ }
}