1

我使用 Wea​​kHashMap 和 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);会变为空。

4

2 回答 2

2

ReentrantReadWriteLock无法控制WeakHashMap垃圾收集器的行为。

WeakHashMap状态类 javadoc

类的行为WeakHashMap部分取决于垃圾收集器的行为,因此一些熟悉的(尽管不是必需的) Map不变量不适用于此类。因为垃圾收集器可能随时丢弃键,aWeakHashMap可能表现得好像一个未知线程正在默默地删除条目。特别是,即使你在一个WeakHashMap实例上同步并且不调用它的任何 mutator 方法,size 方法也有可能随着时间的推移返回较小的值,isEmpty方法返回false然后true方法containsKey返回true并稍后返回false 给定的key,用于get为给定键返回值但稍后返回null的方法put方法返回null和 remove 方法返回false以前出现在映射中的键,并连续检查键集、值集合和条目集以产生连续较少数量的元素。

换句话说,是的,如果垃圾收集器在两个调用之间起作用(并且您没有其他对相应键的强引用),则您的containsKey调用可能会返回trueget随后的调用可能会返回。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
于 2019-04-09T03:57:20.863 回答
1

然后您的代码将返回 null。

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

于 2019-04-09T03:56:02.467 回答