Thread safe without volatile

Can anyone explain why this example is thread safe without mutability?

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

In fact, considering that the computeHashCode function always returns the same result and has no side effects (for example, idempotent), you can even get rid of all 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 understand we donโ€™t care if the value is calculated twice (so this is not truly thread safe). I would also like to know if the new stream created after the hash code is calculated will see the new hash code?

+4
source share
4 answers

It goes on thin ice, but here is the explanation. The visibility problem means that some threads can see the old version, and some - the new one. In our case, some threads see 0 , while others see cachedHashCode .

Themes that call hashCode() and see cachedHashCode will simply return it (the condition if (h == 0) not satisfied), and everything works.

But the threads that see 0 (even though cachedHashCode may have already been computed) will simply reprogram it again.

In other words, in the worst case, each thread will go into the branch, seeing 0 for the first time (for example, if it was ThreadLocal ).

Since computeHashCode() is idempotent (very important), both calling it several times (in different threads) and reassigning it again to the same variable should not have any side effects.

+6
source

Important information here

The computeHashCode function always returned the same result.

If so, then computeHashCode is known to be effectively unchanged, and since it will always be the same value, you will never have a problem with concurrency.

As for the fact that cachedHashCode is unstable. This will not affect thread safety, because you always assign and return a local thread variable h , which will be a non-zero computed HashCode.

+6
source

This is called a bright idiom of the same name. It is used when calculating a value is idempotent (returns the same value each time, the consequence: the type must be unchanged) and cheap (if it is accidentally recounted more than once, this is good). It always takes a line-by-line form.

 class Foo { private Value cacheField; // field holding the cached value; not volatile! public Value getValue() { Value value = cacheField; // very important! if (value == 0 or null or whatever) { value = computeValue(); cacheField = value; } return value; } } 

or something more or less equivalent. If your implementation is not idempotent or not cheap, then you should use another idiom; see "Effective Java Element 71". But the fact is that no more than one threads are read on cacheField , and if they see cacheField in the state when the value was not calculated, they recalculate the value.

As mentioned in Effective Java, String.hashCode() is implemented in this way, for example.

+4
source

This would be true only if Foo is immutable with respect to fields that contribute to the hash code. (which is necessary to satisfy ", assuming the computeHashCode function always returns the same result")

Otherwise, I do not agree that it will be thread safe.

  • Input 1 enters Hashcode (), passes halfway through it, and pauses before returning answer 5 (for example).
  • Topic 2 Introduces computeHashCode () and stops halfway through it
  • Topic 3 modifies the object in a way that affects the hash code.
  • Thread 1 restarts, sets hashcode to 5, and places the object in the hash map as a key.
  • Thread 2 restarts, sets the hash code to 78392.

Topic 1 may or may not find the key on the hash map later, depending on how jvm decided to process this cache code. Jvm has the ability to store separate copies of a non-volatile field if he likes it. Volatile only guarantees that jvm does not do this and that all threads always see the same value.

0
source

Source: https://habr.com/ru/post/1394272/


All Articles