Could the JIT collapse two volatile reads as one in certain expressions?

后端 未结 5 747
礼貌的吻别
礼貌的吻别 2021-01-31 04:11

Suppose we have a volatile int a. One thread does

while (true) {
    a = 1;
    a = 0;
}

and another thread does

w         


        
5条回答
  •  轮回少年
    2021-01-31 04:27

    On one hand the very purpose of a volatile read is that it should always be fresh from memory.

    That is not how the Java Language Specification defines volatile. The JLS simply says:

    A write to a volatile variable v (§8.3.1.4) synchronizes-with all subsequent reads of v by any thread (where "subsequent" is defined according to the synchronization order).

    Therefore, a write to a volatile variable happens-before (and is visible to) any subsequent reads of that same variable.

    This constraint is trivially satisfied for a read that is not subsequent. That is, volatile only ensures visibility of a write if the read is known to occur after the write.

    This is not the case in your program. For every well formed execution that observes a to be 1, I can construct another well formed execution where a is observed to be 0, simply be moving the read after the write. This is possible because the happens-before relation looks as follows:

    write 1   -->   read 1                    write 1   -->   read 1
       |              |                          |              |
       |              v                          v              |
       v      -->   read 1                    write 0           v
    write 0           |             vs.          |      -->   read 0
       |              |                          |              |
       v              v                          v              v
    write 1   -->   read 1                    write 1   -->   read 1                 
    

    That is, all the JMM guarantees for your program is that a+a will yield 0, 1 or 2. That is satisfied if a+a always yields 0. Just as the operating system is permitted to execute this program on a single core, and always interrupt thread 1 before the same instruction of the loop, the JVM is permitted to reuse the value - after all, the observable behavior remains the same.

    In general, moving the read across the write violates happens-before consistency, because some other synchronization action is "in the way". In the absence of such intermediary synchronization actions, a volatile read can be satisfied from a cache.

提交回复
热议问题