Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does ConcurrentHashMap use a local variable `tab` to reference the table?

In ConcurrentHashMap.putVal() (JDK Version: 11; ConcurrentHashMap.java; line 1010)

final V putVal(K key, V value, boolean onlyIfAbsent) {
   if (key == null || value == null) throw new NullPointerException();
   int hash = spread(key.hashCode());
   int binCount = 0;
   for (Node<K,V>[] tab = table;;) {
       ...
   }
   addCount(1L, binCount);
   return null;
}

Why does it use the variable tab to reference the table? Likewise in ConcurrentHashMap.get() (beginning on line 934)

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)))
                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))))
                return e.val;
        }
    }
    return null;
}
like image 292
James Marva Avatar asked Apr 15 '20 09:04

James Marva


2 Answers

If you use table, the instance it points to can change while working on it, which can lead to undefined behavior or exceptions. Thus, you need to "fixate" it locally and use that local variable.

I assume this is done to prevent undefined behavior if it is, which should not be done, used by two threads at once in write mode*. The instance at which table points to can change even in the not-concurrent HashMap.

An alternative to this would be using the keyword synchronized, but that reduces performance.

* You can read from a HashMap in multiple threads without issue if it is not getting manipulated while multiple threads hold it.

like image 143
ASA Avatar answered Oct 12 '22 22:10

ASA


It's easier to see why Java does this in HashMap where the resize() method sets table = newTab. Any method that was reading the table during a resize() operation would have the reference pulled out from under them and reassigned, which would cause unpredictable behavior.

Volatile could ensure a reading method is updated with the latest table; but that is not at all what we want. We want the reading method to continue uninterrupted with the values that were in the table when it began reading.

Synchronized could block reads and writes from happening simultaneously, but with a performance penalty. If we wanted that, we could revert to using Hashtable.

The same basic reasoning applies to ConcurrentHashMap and its more complicated transfer() method which also reassigns the table reference. The reference is copied into a local variable to avoid losing it during reassignment.

like image 2
jaco0646 Avatar answered Oct 13 '22 00:10

jaco0646