Search code examples
javamultithreadingvolatilejava-9java-memory-model

How to understand JDK9 memory model?


I'm learning the JDK9 memory model.

After watching the speech Java Memory Model Unlearning Experience and reading the paper Using JDK 9 Memory Order Modes.

I'm confused about some concepts.

  1. Does opaque immediately guarantee the visibility?

  2. How to understand partial order and total order in the paper?

For the first question, the paper says

It is almost never a good idea to use bare spins waiting for values of variables. Use Thread.onSpinWait, Thread.yield, and/or blocking synchronization to better cope with the fact that "eventually" can be a long time, especially when there are more threads than cores on a system.

So if I write the code:

// shared variable I and VarHandle I_HANDLE which referred to I
public static int I = 0;

public static final VarHandle I_HANDLE;

// Thread-1
I_HANDLE.setOpaque(1);

// Thread-2
while((int) I_HANDLE.getOpaque() == 0){
}

The thread-2 eventually terminate but maybe after a long time?

If so, is there any minimal approach to guarantee thread-2 immediately see the modifying by thread-1? (Release/Acquire?volatile?)


Solution

  • There is no such thing like “immediately” for updates. Even electricity moves with a finite speed. Generally, asking for a perceivable effect within a particular time span is like asking for a particular execution time for an operation. Neither can be guaranteed, as they are properties of the underlying architecture which the JVM can’t change.

    Practically, of course, JVM developers try to make the operations as fast as possible and all that matters to you, as a programmer, is that there is no faster alternative to opaque writes regarding inter-thread visibility of updates. The stronger access modes do not change how fast an update will become visible, they add additional constraints to reordering of reads and writes.

    So in your example, the update will become visible as fast as the architecture and system load will allow1, but don’t ask for actual numbers. No-one can say how long it will take. If you need guarantees in terms of time quantities, you need a special (“real-time”) implementation that can give you additional guarantees beyond the Java Memory Model.


    1 To name a practical scenario: thread 1 and 2 may compete for the same CPU. Thread 1 writes the value and continues to run for the operating system specific time before the task is switched (and it’s not even guaranteed that thread 2 is the next one). This implies that a rather long time may elapse, in both terms, wall clock time and thread 1’s progress after the write. Of course, other threads may also make a lot of progress on the other CPU cores in the meanwhile. But it’s also possible that thread 2’s polling before thread 1 commits the write is the cause of thread 1 not getting a chance to write the new value. That’s why you should mark such polling loops with onSpinWait or yield, to give the execution environment a chance to prevent such scenarios. See this Q&A for a discussion about the difference between the two.