当 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
。