我使用WeakHashMap和ReentrantReadWriteLock实现了缓存, 我的代码是这样的:
class Demo<T, K> {
private final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock();
private final Map<T, K> CACHE = new WeakHashMap<>();
public K get(T t) {
ReentrantReadWriteLock.ReadLock readLock = LOCK.readLock();
ReentrantReadWriteLock.WriteLock writeLock = LOCK.writeLock();
readLock.lock();
if(CACHE.containsKey(t)){
//-- question point --
K result = CACHE.get(t);
readLock.unlock();
return result;
}
readLock.unlock();
K result = // find from db;
writeLock.lock();
CACHE.put(t,result);
writeLock.unlock();
return result;
}
}
我的问题是,发生gc的原因是在if(CACHE.containsKey(t))
之后但在K result = CACHE.get(t);
之前具有读取锁定,并导致if(CACHE.containsKey(t))
为真,但K result = CACHE.get(t);
为空。
答案 0 :(得分:2)
您的ReentrantReadWriteLock
无法控制WeakHashMap
在垃圾收集器方面的行为。
WeakHashMap
状态的类javadoc
WeakHashMap
类的行为部分取决于操作 垃圾收集器,所以很熟悉(虽然不是必需的)Map
不变式不适用于此类。因为垃圾 收集器可以随时丢弃密钥,WeakHashMap
的行为可能与 尽管一个未知线程正在静默删除条目。尤其是, 即使您在WeakHashMap
实例上进行同步并且不调用任何 它的mutator方法,size方法可能会返回 随时间减小的值,以便isEmpty
方法返回false
并 然后true
,使containsKey
方法返回true
,随后返回false
给定键,让get
方法返回给定键的值 但稍后返回null
,让put
方法返回null
并返回 remove方法可返回false
以获取先前似乎是 在地图中,并用于连续检查密钥集,其值 集合,并且该条目设置为连续产生较小数量的 元素。
换句话说,是的,如果垃圾回收器在两次调用之间进行操作,则您的containsKey
调用可能返回true
,随后的get
返回false
这两个调用(并且您没有其他对相应密钥的强引用)。
您可以使用类似这样的小程序来验证此行为
public class Example {
public static void main(String[] args) throws Exception {
WeakHashMap<Example, Integer> CACHE = new WeakHashMap<>();
CACHE.put(new Example(), 2);
if (CACHE.containsKey(new Example())) {
System.gc();
System.out.println("missing? " + CACHE.get(new Example()));
}
}
@Override
public int hashCode() {
return 42;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
可打印
missing? null
答案 1 :(得分:1)
然后您的代码将返回null。
如果这不是您想要的,只需执行get()调用并检查您是否得到了非null的结果。如果您担心返回null,那么像在此处那样调用containsKey()没有任何好处。