ConcurrentHashMap.get()如何防止脏读?

Ziz*_*izy 3 java concurrency concurrenthashmap

我正在查看源代码ConcurrentHashMap,想知道该get()方法在没有任何监视器的情况下如何工作,代码如下:

public V get(Object key) {
        Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
        int h = spread(key.hashCode());
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (e = tabAt(tab, (n - 1) & h)) != null) { 
            if ((eh = e.hash) == h) {
                if ((ek = e.key) == key || (ek != null && key.equals(ek))) // mark here for possible dirty read
                    return e.val;
            }
            else if (eh < 0)
                return (p = e.find(h, key)) != null ? p.val : null;
            while ((e = e.next) != null) {
                if (e.hash == h &&
                    ((ek = e.key) == key || (ek != null && key.equals(ek)))) // mark here for possible dirty read
                    return e.val;
            }
        }
        return null;
    }
Run Code Online (Sandbox Code Playgroud)

我标记的两行正在做同样的事情:检查key当前的是否Node<K, V>等于所需的key。如果true,将返回其对应的值。但是如果另一个线程从数据结构中切入之前return和这个节点怎么办?remove()由于局部变量e仍然保存着被删除节点的引用,GC 将保留它,并且该get()方法仍然会返回被删除的值,从而导致脏读。

我错过了什么?

kay*_*ya3 6

它没有

检索操作(包括get)通常不会阻塞,因此可能与更新操作(包括putremove)重叠。检索反映了最近完成的更新操作在其开始时的结果。(更正式地说,给定键的更新操作与报告更新值的该键的任何(非空)检索具有发生之前关系。)

这通常不是问题,因为永远不会返回如果该方法获取锁就get不可能发生的结果,从而阻塞其他线程中的更新操作。get您只需得到结果,就好像get调用发生在更新操作开始之前一样。

因此,如果您不介意它是get在更新之前还是之后发生,那么您也不应该介意它在更新期间发生,因为在期间之前之间没有明显的差异。如果您确实希望在更新get出现这种情况,那么您需要从更新线程发出更新已完成的信号;无论如何,等待获取锁都无法实现这一点,因为您可能会在更新发生之前获得锁(在这种情况下,您将获得与未获取锁相同的结果)。