In ConcurrentHashMap
of JDK8, both of the methods tabAt
and setTabAt
need volatile semantics.
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectVolatile(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectVolatile(tab, ((long)i << ASHIFT) + ABASE, v);
}
The author commented that:
Note that calls to setTabAt always occur within locked regions, and so in principle require only release ordering, not full volatile semantics, but are currently coded as volatile writes to be conservative.
Then in ConcurrentHashMap
of JDK11, both of the methods tabAt
and setTabAt
don't need volatile semantics and use acquire and release semantics instead.
static final <K,V> Node<K,V> tabAt(Node<K,V>[] tab, int i) {
return (Node<K,V>)U.getObjectAcquire(tab, ((long)i << ASHIFT) + ABASE);
}
static final <K,V> void setTabAt(Node<K,V>[] tab, int i, Node<K,V> v) {
U.putObjectRelease(tab, ((long)i << ASHIFT) + ABASE, v);
}
The author commented that:
Note that calls to setTabAt always occur within locked regions, and so require only release ordering.
As far as I know, the elements of the array Node<K,V>[] tab
aren't volatile
. Using only acquire and release semantics can't guarantee immediate visibility. The thread that gets elements can't immediately see the update made by the thread that sets elements.
Even if the calls to setTabAt
always occur within locked regions, it seems that it doesn't make any difference. Because the calls to tabAt
don't always occur within locked regions, there is no guarantee of happens-before relationship.
The call to tabAt
doesn't occur within a locked region in the method 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)))
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;
}
I wonder if there is something wrong with my understanding. Could anyone provide more information about this?