java - Java SoftReference 奇怪的行为
问题描述
Map<E, SoftReference<T>> cache = new ConcurrentHashMap<E, SoftReference<T>>();
我已经 map 声明了一个类似于上面的地图,我将其用作缓存。
问题是我要在将项目添加到缓存后立即在缓存上执行所有操作,而不是稍后。
例如:
cache.add("Username", "Tom");
if(cache.contains("Username"))
返回 true 但
String userName = (String)cache.get("Username")
返回空值。
这只有在很长一段时间后才会发生。
如果我在将它添加到缓存几个小时后得到了这个值,我就得到了正确的值。如果我在很长一段时间后得到这个值,比如超过 15-20 小时,我会得到空值。
GC清除SoftReference对象时,key会留在HashMap中吗?这是这种行为的原因吗?
解决方案
当 a 的引用SoftReference
被垃圾回收时,SoftReference
被清除,即它的引用字段被设置为null
。
因此,不仅 key 保留在 map 中,关联的 valueSoftReference
实例也保留在 map 中,即使它的引用字段是null
.
Map<E, SoftReference<T>>
但是,由于在您声明的字段和 and 的调用者cache.add("Username", "Tom")
之间必须有一个层(String)cache.get("Username")
,而您没有显示,因此该层甚至可能正确处理它。
为了完整起见,正确的实现可能看起来像
final Map<E, SoftReference<T>> cache = new ConcurrentHashMap<>();
/** remove all cleared references */
private void clean() {
cache.values().removeIf(r -> r.get() == null);
}
public void add(E key, T value) {
clean();
cache.put(key, new SoftReference<>(value));
}
public boolean contains(E key) {
clean();
return cache.computeIfPresent(key, (e,r) -> r.get()==null? null: r) != null;
}
public T get(E key) {
clean();
for(;;) {
SoftReference<T> ref = cache.computeIfPresent(key, (e,r) -> r.get()==null? null: r);
if(ref == null) return null;
T value = ref.get();
if(value != null) return value;
}
}
此代码确保在查询时自动删除收集的值的映射。此外,虽然不是必需的,但该clean()
操作会删除地图的所有收集条目,以减少地图的空间(类似于WeakHashMap
内部的工作方式)。
但请注意,仍然不能保证当cache.contains("Username")
返回时true
,后续cache.get("Username")
将返回非null
值。这个问题也被称为check-then-act反模式,它可能会失败,因为在检查和后续操作之间,可能会发生并发更新(即使您仅通过一个线程使用缓存,因为垃圾收集可能异步发生),过时的前面测试的结果。
在这方面,该contains
操作对于大多数场景是无用的。您必须调用get
, 以接收对值的强引用,并继续使用该引用,如果不是null
。
推荐阅读
- r - 将 tidyselect 辅助函数添加到向量
- python - tkinter photoimage 从另一个文件中获取图像
- python - 如何将这行代码转换为 for 循环?
- json - 将 JSON 数据写入文本文件,但进入堆空间
- php - 根据使用的命令和一些未知的上下文,相同的字节序列如何显示不同?
- java - 如何过滤多个参数的流数据?
- python - python排序方法中的key如何使用列表
- javascript - 如何为初学者创建一个存储用户数据的登录系统?
- python - Pandas:根据子集在另一个数据框中查找重复项
- apache-spark-sql - spark sql 协议缓冲区支持