简体   繁体   English

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

[英]How does ConcurrentHashMap.get() prevent dirty read?

I am looking at the source code of ConcurrentHashMap and wondering how the get() method works without any monitor, here's the code:我正在查看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;
    }

The two lines I marked are doing the same thing: checking if the key of the current Node<K, V> equals to the key needed.这两条线标志着我都在做同样的事情:检查是否key当前的Node<K, V>等于的key需要。 If true , will return its corresponding value.如果为true ,将返回其对应的值。 But what if another thread cuts in before return and remove() this node from the data structure.但是如果另一个线程在returnremove()之前从数据结构中切入这个节点呢? Since the local variable e is still holding the reference of the removed node, GC will leave it be and the get() method will still return the removed value, thus causing a dirty read.由于局部变量e仍然持有被移除节点的引用,GC 会保留它,而get()方法仍然会返回被移除的值,从而导致脏读。

Did I miss something?我错过了什么?

It doesn't : 它没有

Retrieval operations (including get ) generally do not block, so may overlap with update operations (including put and remove ).检索操作(包括get )通常不会阻塞,因此可能与更新操作(包括putremove )重叠。 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.) (更正式地,给定键的更新操作与报告更新值的该键的任何(非空)检索具有先发生关系。)

This is generally not a problem, since get will never return a result that couldn't have happened if the get method acquired a lock, blocking the update operation in the other thread.这通常不是问题,因为如果get方法获取了锁, get永远不会返回不可能发生的结果,从而阻塞其他线程中的更新操作。 You just get the result as if the get call happened before the update operation began.您只会得到结果,就好像get调用发生在更新操作开始之前一样。

So, if you don't mind whether the get happens before or after the update, you also shouldn't mind it happening during the update, because there is no observable difference between during and before .所以,如果你不介意get发生在更新之前还是之后,你也不应该介意它发生更新期间,因为duringbefore之间没有明显的区别。 If you do want the get to appear to happen after the update, then you will need to signal from the updating thread that the update is complete;如果您确实希望get在更新出现,那么您需要从更新线程发出更新完成的信号; waiting to acquire a lock wouldn't achieve that anyway, because you might get the lock before the update happens (in which case you'd get the same result as if you didn't acquire the lock).等待获取锁无论如何都不会实现,因为您可能会在更新发生之前获得锁(在这种情况下,您将获得与未获取锁相同的结果)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM