Can anyone explain why this example is thread safe without volatile?
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
In fact, assuming that the computeHashCode function always returned the same result and had no side effects (i.e., idempotent), you could even get rid of all of the synchronization.
// Lazy initialization 32-bit primitives
// Thread-safe if computeHashCode is idempotent
class Foo {
private int cachedHashCode = 0;
public int hashCode() {
int h = cachedHashCode;
if (h == 0) {
h = computeHashCode();
cachedHashCode = h;
}
return h;
}
// other functions and members...
}
MORE: I get it, we don't care if the value is computed twice (so it is not truly thread safe). I also like to know if new threads created after the hashcode has been calculated is guaranteed to see the new hashcode?
This is walking on a thin ice, but here is the explanation. Visibility problem means that some threads might see old version and some - new one. In our case, some threads see 0
while others - cachedHashCode
.
Threads that call hashCode()
and see cachedHashCode
will just return it (if (h == 0)
condition is not met) and everything works.
But threads that see 0
(despite the cachedHashCode
might have already been computed) will just recompute it again.
In other words, in the worst case scenario, every thread will enter the branch seeing 0
for the first time (like if it was ThreadLocal
).
Since computeHashCode()
is idempotent (very important), both calling it several times (by different threads) and reassign it again to the same variable shouldn't have any side effects.
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