Volatile concept

Volatile is a type specifier. Volatile is used as an instruction key to ensure that the instruction is not omitted for compiler optimization, and requires that the value be read directly each time. Volatile variables mean that they can be changed unexpectedly so that the compiler does not make assumptions about their value. — Baidu Encyclopedia

Therefore, it has two main functions: one is thread visibility (to ensure that different threads operate on the variable is visible, i.e. one thread changes the value of a variable, the new value is immediately visible to other threads). One is to prevent instruction reordering. To understand this, we need to understand one of our Java Memory Models (JMM).

Java Memory Model

We know that in Java, instance fields, static fields, and array elements are stored in heap memory, which is shared by threads, while other virtual machine stacks, etc., have some contents that are thread exclusive and don’t have memory visibility problems or are subject to memory models. Communication between Java threads is controlled by the Java memory model, where the JMM determines when a write to a shared variable by one thread is visible to another. From an abstract point of view, JMM defines an abstract relationship between threads and main memory: Shared variables between threads are stored in main memory, and each thread has a private local memory where it stores copies of shared variables to read/write. The Java memory model is abstracted as follows:

The thread retrieves a variable from main memory into its own local memory, and then overwrites the new value to main memory. Then another thread picks up the variable and gets a new value. In this way, a communication between different threads is achieved, and the communication process must pass through main memory. The JMM provides Java programmers with memory visibility assurance by controlling the interaction between main memory and local memory for each thread.

The thread is visible

When one thread changes the value of a shared variable, other threads are immediately aware of the change, thus ensuring the synchronization of a single read/write operation.

** Example 1: What would the value of **j be?

// Thread A executes the code
k = 5;

// Thread B executes the code
int j = k;
Copy the code

The answer is no. Because even though thread A has updated k to 5, the operation is done in thread A’s local memory. The updated variables in local memory are not immediately synchronized back to main memory, so the value of k obtained by thread B from main memory is uncertain. This is the visibility problem. After thread A makes changes to variable K, thread B does not immediately see the value changed by thread A.

Example 2: Will the new thread print end?

public class Test {
    private static /*volatile*/ boolean flag = true
    public static void main (String[] args) throws I interrupted Exception {
        new Thread(()-> {
            while (flag) {
                //do sth} the System, the out, println ("end");
        },name: "server") .start();

        Thread.sleep( millis: 1GGG);
        flag = false}}Copy the code

The answer is no, the new thread’s local memory has a true flag, which it always uses. Even though the main thread has changed the flag and synchronized it to main memory. The local space of the new thread already has a flag and will no longer access the main space. Using the volatitle keyword to ensure that changes are synchronized immediately and that the variable is retrieved from the main memory each time it is used elsewhere.

What you can probably see from the two examples is that volatile variables are modified, changes are updated and threads are accessed in main rather than locally

Instruction reordering

In effect, compilers and processors often reorder instructions when executing programs to improve performance.

  1. Compiler optimized reordering. The compiler can rearrange the execution order of statements without changing the semantics of a single-threaded program.
  2. Instruction – level parallel reordering. Modern processors use instruction-level Parallelism (ILP) to overlap multiple instructions. If there is no data dependency, the processor can change the execution order of the machine instructions corresponding to the statement.
  3. Memory system reordering. Because the processor uses caching and read/write buffers, this makes the load and store operations appear to be out of order.

Here is an example to show the existence of reorder, look at the following pseudocode:

a=0,b=0,x=0,y=0    
Copy the code
/ / thread
a=1;
x=b;
Copy the code
/ / thread 2
b=1;
y=a;
Copy the code

Let’s say reordering doesn’t happen. So in order to execute, two threads must have four instructions at least a=1 must be before x=b, and b=1 must be before y=a. There are six possible execution sequences

Thread 2 does not start until thread 1 finishes executing
a=1;
x=b;
b=1;
y=a;
/ / results x = 0, y = 1

Thread 2 has finished executing when thread 2 starts
b=1;
y=a;
a=1;
x=b;
/ / the x = 1, y = 0
Copy the code
// case 3 :(and) two threads are executed interlaced
a=1;
b=1;
x=b;
y=a;
/ / the x = 1, y = 1

// case 4 :(and) two threads are executed interlaced
b=1;
a=1;
y=a;
x=b;
/ / the x = 1, y = 1

Thread 2 starts and finishes
a=1;
b=1;
y=a;
x=b;
/ / the x = 1, y = 1

// case 5 :(and) thread 2 is executing midway. The thread starts and finishes
b=1;
a=1;
x=b;
y=a;
/ / the x = 1, y = 1
Copy the code

In the case where the order is not adjusted, there are no more than three results (1,0), (0,1), (1,1), but the actual result will be x=0,y=0. That is, thread instructions are out of order and will be adjusted. For a single thread, tweaking doesn’t affect the result. It just improves efficiency, like omitting instructions that cancel out one plus one minus one or tweaking the order. It doesn’t affect the result.

/* There will be no reordering for the following three groups because changing the order will affect the result */
a=1;
b=a;

a=1;
a=2;

a=b;
b=1;
Copy the code

In the two-thread example above, either thread 1 with a=1 and x=b or thread 2 with b=1 and y=a. The two statements are not dependent on each other in their own thread, so the swap does not affect the result and so the swap occurs. But two threads put together are dependent on variables, and the result is inconsistent due to rearrangement. So in multithreading you have problems and you have to disable reordering, and using volatile you can disable reordering by adding memory barriers between instructions.

Volatile. Okay? | creators camp The campaign is under way…