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

How does ConcurrentHashMap.get() prevent dirty read?

我正在查看 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;
    }

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

我是不是漏掉了什么?

It doesn't:

Retrieval operations (including get) generally do not block, so may overlap with update operations (including put and remove). Retrievals reflect the results of the most recently completed update operations holding upon their onset. (More formally, an update operation for a given key bears a happens-before relation with any (non-null) retrieval for that key reporting the updated value.)

这通常不是问题,因为 get 永远不会 return 如果 get 方法获取了锁就不会发生的结果,阻塞更新操作另一个线程。您得到的结果就好像 get 调用发生在更新操作开始之前。

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