WeakHashMap和ReentrantReadWriteLock

时间:2019-04-09 03:39:49

标签: 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 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);为空。

2 个答案:

答案 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()没有任何好处。