How does ConcurrentHashMap.get() prevent dirty read?

Zizy :

I am looking at the source code of ConcurrentHashMap and wondering how the get() method works without any monitor, here's the code:

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. If true, will return its corresponding value. But what if another thread cuts in before return and remove() this node from the data structure. 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.

Did I miss something?

kaya3 :

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.)

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. You just get the result as if the get call happened before the update operation began.

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. 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; 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).

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=4441&siteId=1