Why and how does volatility imply atomic reads / writes?

Firstly, I know that volatile does not do multiple operations (like i++ ) atomic. This question is about a single read or write operation.

My initial understanding was that volatile only strengthens the memory barrier (i.e. other threads will be able to see updated values).

Now I noticed that the JLS section 17.7 says that volatile additionally does one read or write of an atom. For example, given two streams, both write a different value to volatile long x , then x will finally be one of the values.

I am curious how this is possible. In a 32-bit system, if two streams are written to a 64-bit location in parallel and without the โ€œcorrectโ€ synchronization (i.e., some kind of blocking), it should be possible for the result to be mixed. For clarity, let me use an example in which stream 1 writes 0L, while stream 2 writes -1L to the same 64-bit memory location.

 T1 writes lower 32 bit T2 writes lower 32 bit T2 writes upper 32 bit T1 writes upper 32 bit 

The result may be 0x0000FFFF, which is undesirable. How volatile prevent this scenario?

I also read elsewhere that this usually does not degrade performance. How can I synchronize recordings with little impact of speed?

+6
source share
3 answers

Your claim that volatile uses only a memory barrier (meaning, clears the processor cache) is false. It also implies the relationship between read and write combinations of volatile values. For instance:

 class Foo { volatile boolean x; boolean y; void qux() { x = true; // volatile write y = true; } void baz() { System.out.print(x); // volatile read System.out.print(" "); System.out.print(y); } } 

When running both methods from two threads, the above code will either print true false , true true , or false false , but never false true . Without the volatile keyword, you are not guaranteed a later condition, because the JIT compiler can reorder the statements.

Just as the JIT compiler can provide this condition, it can protect a 64-bit value that is read and written to the assembly. volatile values โ€‹โ€‹are explicitly processed by the JIT compiler to ensure they are atomic. Some processor instruction sets support this directly with specific 64-bit instructions, otherwise the JIT compiler mimics it.

The JVM is more complex, as you might expect, and this is often explained without full capability. Consider reading this wonderful article that covers all the details.

+4
source

volatile ensures that what the stream reads is the last value at this point, but does not synchronize the two records.

If a stream writes a normal variable, it stores the values โ€‹โ€‹in the stream until some specific events occur. If a stream writes a volatile variable, it immediately changes the variable's memory.

0
source

In a 32-bit system, if two streams are written to a 64-bit location in parallel and without the โ€œcorrectโ€ synchronization (that is, some kind of blocking), it should be possible for the result to be mixed

This can really happen if the variable is not marked volatile . Now, what does the system do if the field is volatile ? Here is a resource that explains this: http://gee.cs.oswego.edu/dl/jmm/cookbook.html

Almost all processors support at least a coarse-grained barrier instruction, often called a โ€œFenceโ€, which ensures that all loads and stores launched before the fence are strictly ordered before any loading or storage begins after the fence [... ], if available, you can implement the volatile storage in the form of an atomic instruction (for example, XCHG on x86) and lower the barrier. This can be more effective if atomic instructions are cheaper than StoreLoad barriers.

In essence, processors provide the means to implement the warranty, and the available tool depends on the processor.

0
source

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


All Articles