Java WeakHashMap和可重入式写锁

Java WeakHashMap和可重入式写锁,java,garbage-collection,jvm,reentrantreadwritelock,Java,Garbage Collection,Jvm,Reentrantreadwritelock,我使用WeakHashMap和ReentrantReadWriteLock实现了一个缓存, 我的代码是这样的: class Demo<T, K> { private final ReentrantReadWriteLock LOCK = new ReentrantReadWriteLock(); private final Map<T, K> CACHE = new WeakHashMap<>(); public K get(T

我使用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;
    }
}
类演示{
私有最终ReentrantReadWriteLock锁=新的ReentrantReadWriteLock();
私有最终映射缓存=新WeakHashMap();
公共K-get(T){
REENTRANDREADWRITELOCK.ReadLock ReadLock=LOCK.ReadLock();
ReentrantReadWriteLock.WriteLock WriteLock=LOCK.WriteLock();
readLock.lock();
if(CACHE.containsKey(t)){
//--问题点--
K result=CACHE.get(t);
readLock.unlock();
返回结果;
}
readLock.unlock();
K result=//从数据库中查找;
writeLock.lock();
CACHE.put(t,result);
writeLock.unlock();
返回结果;
}
}

我的问题是,在
if(CACHE.containsKey(t))
之后但在
K result=CACHE.get(t)之前执行gc会发生什么情况
为真,但
K result=CACHE.get(t),则导致
获取空值。

那么您的代码将返回空值


如果这不是您想要的,只需执行get()调用并检查是否得到非null结果。如果担心返回null,那么在这里调用containsKey()并没有什么好处

您的
ReentrantReadWriteLock
无法控制
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;
    }
}

作为一个侧节点,您使用锁定的方式在将来会给您带来问题(例如,两次解锁,没有保护efc)。在lock类的javadoc中,有一个示例说明了如何安全地使用锁(通过使用try-finally块)。只需使用
K result=CACHE.get(t);if(result!=null)返回结果。问题解决了。除此之外,您正在释放读锁,然后获取写锁;如果另一个线程将一个值正好放在这两个步骤之间,您将覆盖映射。一旦获得写入锁,您必须重新检查。考虑<代码> PuthItgue(如果你使用java 8)。霍格尔谢谢你的建议,非常有用。
missing? null