Categories
Uncategorized

volatile principle underlying implementation

Foreword

When the shared variable is declared as volatile, read this variable / write operations will be very special, here we uncover the mystery of volatile.

1.volatile semantic memory

1.1 volatile characteristics

A volatile variable itself has the following three characteristics:

  1. Visibility: that is, when a thread modifies the declared value of the volatile variable, the new values ​​for the other to read the variable thread is immediately visible. The common variables can not do this, the value of the common variable transmission needs to be done by the main memory between threads.

  2. Order: the ordering of the so-called volatile variable is declared as a critical section of code volatile variables execution is in order, which prohibits instruction reordering.

  3. Limited Atomicity: Atomicity Atomicity here synchronized with the volatile variables is different, means synchronized atomic declared as synchronized as long as the pieces of code or method of atomic operations performed on. The method does not modify the volatile or pieces of code, which is used to modify variables, to read a single volatile variables / write operations are atomic, but similar to such composite operations are not volatile ++ atomic. Atomicity is volatile so limited. And in a multithreaded environment, volatile does not guarantee atomicity.

1.2 volatile write – read semantic memory

Semantic volatile memory write: Write when writing to a volatile variable thread, JMM will correspond to the thread local memory shared variable values ​​flushed to main memory.

volatile memory semantics to read: When reading a volatile variable reader thread, the thread will JMM corresponding local memory is deasserted, the next thread to read from the main memory shared variables.

The principle of semantic 2.volatile

Before introducing the volatile semantic realization of the principle, let’s look at two CPU-related terminology:

    Memory barrier (memory barriers): a set of processor instructions for implementing restrictions on the order of memory operations.

    Cache line (cache line): smallest unit of storage that can be allocated in the CPU cache. The processor cache line fill will load the entire cache line.

2.1 volatile achieve visibility principle

How volatile memory visibility semantics to achieve it? Here we look at a piece of code, assembly instructions and code generation processor to print out (assembly instructions on how to print, I will attach the end of the article), look for volatile variables during a write operation, CPU will do anything:

public class VolatileTest {

    private static volatile VolatileTest instance = null;

    private VolatileTest(){}

    public static VolatileTest getInstance(){
        if(instance == null){
            instance = new VolatileTest();
        }

        return instance;
    }

    public static void main(String[] args) {
        VolatileTest.getInstance();
    }
}

The above code is the one we are very familiar with singleton mode code in a multithreaded environment does not guarantee thread-safe code in a special place, I will add a volatile instance variable instance modifications, see the following assembly instructions printed :

In the screenshot above, we see the end of a line is crossed I have a compilation of comments: putstatic instance, understand the small partners JVM bytecode instructions are aware, putstatic meaning is to set the value of a static variable, also in the above code is assigned to the static instance variable, the code corresponding to: instance = new VolatileTest (); instance is instantiated in getInstance method, since the addition of volatile modification instance, the setting value to a static instance variable is written in a volatile variable.

See above there are assembly instructions, there bytecode instructions, you will not confuse these two commands, here I indicate what bytecode instructions and assembly instructions difference:

We all know that java is a cross-platform language, java is how to achieve such platform independence it? This requires us to understand the JVM and java bytecode files. Here we need a little consensus that any one programming language can be converted to be able to finally be executed by hardware as assembly instructions related to the platform, such as C and C ++ are our source code directly compiled into the assembly instructions associated with the CPU to CPU execution. Different CPU architectures different series, they have different assembly instructions, such as corresponding to X86 X86 CPU architecture assembly instructions, corresponding to the CPU architecture arm arm assembly instructions. If the program source code is compiled directly into the underlying hardware-related assembly instructions, then the cross-platform program will be greatly reduced, but relatively high execution performance. To achieve platform independence, the java compiler javac the java not directly translated into the assembler source program instruction associated with the platform, but is compiled into an intermediate language, i.e., the java byte code class files. Byte code file, the name suggests bytecode is stored, i.e. a one byte. It has opened read java bytecode file is too small partners might find bytecode files kept inside is not binary, but hexadecimal, binary because it is too long, a byte to the 8 binary components. Therefore subscripts represent hexadecimal, two hexadecimal byte can represent a. Byte code file is compiled java source code can not be directly executed CPU, then how to enforce it? The answer is JVM, in order to allow java program can run on different platforms, the official java java virtual machine specific to each platform, JVM running on the hardware layer, shielding the differences of various platforms. Byte code files to the unified javac compiler loaded by JVM, and finally reconverted hardware-related machine instruction is executed CPU. Know to load JVM bytecode files, then there is a problem is how to JVM bytecode each byte and we write java source code associated with that is that we know how to write JVM java source the code corresponding to the class file which paragraph hex, this hex is doing, to perform what function? And a bunch of hex, we can not read ah. So this need to define a JVM-level specification, the JVM level of abstraction that we can recognize some instruction mnemonic, mnemonic instructions that the java bytecode instructions.

Look at the screenshot above, when writing this volatile instance variable, found in front add to add a lock command, I box in the screenshot out, how volatile without modification, there is no lock in.

lock instruction causes the following event in the multi-core processors:

The current data processor cache line is written back to system memory, while the other CPU caches data set in the memory address is not valid.

In order to increase the processing speed of the processor and memory generally do not communicate directly, but first memory system to read data before an internal cache operation, but the operation is completed when the processor does not know the write cache data back to memory. But if the addition of volatile variables write, JVM will send a lock prefix instructions to the processor, this variable in the data cache line is written back to system memory. Then just write back to the system memory, but the data cache line in other processors or old, to make the other data processor cache line is written back to the new system memory data, we need to implement cache coherency protocol. A data processor that will own cache line is written back to system memory after each other processors to check if they will buffer the data has expired by sniffing data traveling in the bus, when the processor finds himself after memory address data corresponding to the cache line is modified, it will own cache line buffer as invalid data, when the operation of the processor to be modified on this data, the re-read data from system memory to your cache line, re-cache.

Under summary: volatile visibility is achieved by means of a lock CPU instruction, before writing the volatile by machine instructions plus lock prefix, the write volatile with the following two principles:

    Volatile write cache processor will be written back to main memory.

    A processor’s cache is written back to memory can cause failure of the other processor’s cache.

2.2 volatile orderly implementation principle

volatile ordering to ensure that by prohibiting instruction reordering to achieve. Reordering instruction including a compiler and a processor reordering, JMM limit both instructions are reordered.

Banning instruction reordering is how to achieve it? The answer is to add memory barrier. JMM has the following four cases to add volatile memory barrier:

    Each preceding write operation to insert a volatile StoreStore barrier against volatile write operation and the write reorder later.

    Write back operation is inserted in each of a volatile StoreLoad barrier against volatile write and read operations reordering later.

    Inserting a volatile LoadLoad barrier after each read operation, a read operation to prevent volatile read and reordered later.

    Inserting a volatile LoadStore barrier after each read operation, a write operation to prevent volatile read and reordered later.

The above-mentioned memory barrier insertion strategy is very conservative, such as a volatile write back operations need to add StoreStore and StoreLoad barrier, but the volatile write back may not read, so in theory you can only add StoreStore barrier, indeed, some processors did. But JMM this conservative strategy can insert memory barriers to ensure that any processor platform, volatile variables are ordered.

3.JSR-133 enhanced volatile memory semantics

JSR-133 before the old java memory model, although the operation is not allowed between the reordering volatile variables, but allows for reordering volatile variable between normal and variable. A barrier such as memory write operation is in front of volatile variables, the latter memory barrier operation is a write operation of the common variable, even though the two write operations may damage volatile memory semantics, but these two operations are allowed JMM reordering .

In JSR-133 and the following new java memory model to enhance the volatile memory semantics. As long as reordering between the volatile variables and common variables may destroy volatile memory semantics, this reordering will be compiled discouraged collation processor and memory barrier access policy prohibits.

Annex: Configure Print assembler instruction idea

Kit Download: link: https: //pan.baidu.com/s/11yRnsOHca5EVRfE9gAuVxA
    Extraction code: gn8z

Download extract the kit, to the bin directory under jre path jdk installation directory, as shown:

Then configure the idea, the input VM options options: -server -Xcomp -XX: + UnlockDiagnosticVMOptions -XX: + PrintAssembly -XX: CompileCommand = compileonly, * the name of the class method names.

JRE option to select jre path has been placed in the toolkit.

Below is my idea configuration:

After running the above can be configured to print the assembly instructions.

Leave a Reply