Search code examples
javasynchronizationshared-memoryvolatilememory-corruption

Java volatile variables affecting memory consistency of other non-volatile variables


Scenario A

A1. Write to a volatile variable

A2. Flush all local non-volatile variable writes to main memory

Scenario B

B1. Read from a volatile variable

B2. Reload all non-volatile variables from main memory to local memory

  • Are scenarios A and B the correct behavior involved with volatile variables? Or does Scenario A also include B2, or does Scenario B also include A2?
  • Are these scenarios atomic? Can anything else happen in between A1 and A2? Or B1 and B2?

(using Java 1.8 / 1.5+)


Solution

  • Writing to a volatile variables does not guarantee to flush non-volatile variables1. However, it will introduce a "happens before" relation between the write to the volatile and any subsequent read of the volatile (assuming no intervening writes to it). You can exploit this as follows:

    1. Thread A : write NV
    2. Thread A : write V
    3. Thread B : read V
    4. Thread B : read NV

    If the actions happen in that order, then Thread B will see updated value of NV in step 4. However, if something (including A) writes to NV after step 2, it is unspecified what Thread B will see at step 4.

    In general, using volatiles in this way requires deep and careful reasoning. It is easier and more robust to use synchronized.


    Your example is unclear:

    • If it is intended to be a description of what the Java programmer must do, it is wrong / nonsensical. Java code cannot flush variables.

    • If it is intended to be a specification of what must happen at the implementation level (e.g. in the JIT compiled code), it is also wrong.

    • If it is intended to be a description of what could happen at the implementation level (e.g. in the JIT compiled code), it is correct.

    I'm not just being pedantic here. The compiler may decide that it doesn't need to flush all local non-volatiles in Thread A, and it will most likely only reload the ones that it needs in Thread B. How it decides? That's the compiler writers' business!


    1 - The JLS does not require hardware specific operations such as flushes. Instead, it requires the compiled code to meet some specific guarantees of memory visibility, and leaves the implementation to the compiler writer.