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;
}
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With