The volatile keyword
Main functions:
1. Ensure visibility between data.
2. Disable command reordering.
1. The visibility
2. Take a quiz
public class VolatileTest implements Runnable {
// The thread terminates when false
private static /*volatile*/ boolean flag = true;
private static int value = 100;
@Override
public void run(a) {
// TODO Auto-generated method stub
while(flag) {
value++;
//System.out.println(value); // You can uncomment it and try
}
System.out.println(Thread.currentThread().getName()+"The end");
}
public static void main(String[] args) throws InterruptedException {
new Thread(new VolatileTest() ).start();
Thread.sleep(1000);
new Thread(new Runnable() {
@Override
public void run(a) {
// TODO Auto-generated method stub
flag = false;
System.out.println(Thread.currentThread().getName()+"The end");
}
}).start();
Thread.sleep(1000);
System.out.println(Thread.currentThread().getName()+"The end");
System.out.println("flag="+flag); }}Copy the code
Results:
Thread-1 End Main End flag=falseCopy the code
We can see that the second thread changes flag to false, but the first thread does not stop running.
1. Multi-threaded memory model
3. Why?
From the first figure, we can see that each thread has a working memory. When the thread runs, it will read data from the main memory to the working memory, then use the working memory, and execute the data in the save to the working memory. However, other threads are unaware of the main memory change and do not know that the main memory flag has changed to false, so they do not update the flag in their own workspace (because there is no operation to read data from main memory), resulting in the flag being true, so the loop cannot terminate.
4. What volatile does: It forces them to read main memory instead of the workspace. Multiple threads use the same space, thus ensuring visibility.
5. Volatile guarantees visibility but not atomicity. Additional operations are required to implement.
If volatile is added to flag.
Results:
Thread-1The end of the Thread -0End Main End flag=false
Copy the code
2. Disallow instruction reordering (orderliness)
Volatile disallows the JVM and processor from reordering instructions for volatile modifiers, but the instructions before and after the volatile modifier are not specified.
What is reordering:
Under single thread: The JVM optimizes our code to improve execution efficiency by changing the location of our instructions, but only if the logic stays the same under single thread. For example:
int a = 1;
int b = 2;
int c = a + b;
/ / replacement for
int b = 2;
int a = 1;
int c = a + b;
Copy the code
This change does not change the result (in single-threaded mode).
At the same time for THE CPU, in order to meet the efficiency problem, we will also reorder our instructions to improve the efficiency of the CPU pipeline.
Reordering rules:
int a = 1;
int b = 2;
volatile int c = 3;
int d = 4;
int f = 6;
Copy the code
Volatile disallows reordering, but only for modifiers. For the above program, a and B have no modifiers, so a and B can be reordered, as can D and f. Ab and df do not reorder because volatile creates a memory barrier.
(1) Insert a StoreStore barrier before volatile writes. Ensure that all previous normal writes are flushed to memory before volatile writes.
(2) Insert a StoreLoad barrier after volatile writes. Avoid reordering volatile writes from potentially volatile reads and writes that follow.
(3) Insert a LoadLoad barrier after volatile reads. Avoid reordering of volatile reads and subsequent plain reads.
(4) Insert a LoadStore barrier after volatile reads. Avoid reordering of volatile reads and subsequent plain writes.
In simple terms, the preceding instructions for volatile modifier do not reorder the following instructions, and when volatile modifier is used at the same time, the preceding code is completely executed.
Here’s an example of reordering:
public class Disorder {
private static int x = 0, y = 0;
private static int a = 0, b = 0;
public static void main(String[] args) throws InterruptedException {
int count = 0;
long start = System.currentTimeMillis();
while(true){
count++;
x = 0; y = 0;
a = 0; b = 0;
Thread one = new Thread(() -> {
a = 1;
x = b;
});
Thread other = new Thread(() -> {
b = 1; y = a; }); one.start(); other.start(); one.join(); other.join();if (x == 0 && y ==0) {long end = System.currentTimeMillis();
System.out.println("Number of program runs:"+count);
System.out.println("Program time:"+(end-start));
break; }}}}Copy the code
Suppose that the program is executed line by line, that is, the order does not change.
Case 1 (one) | Case 1 (other) | Case 2 (one) | Case 2 (other) | 3 (one) | 3 (other) | |
---|---|---|---|---|---|---|
a = 1 | a = 1 | a = 1 | ||||
x = b | b = 1 | b = 1 | ||||
b = 1 | x = b | y = a | ||||
y = a | y = a | x = b | ||||
The results of | a=1 x=0 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
4 (one) | 4 (other) | 5 (one) | 5 (other) | 6 (one) | 6 (other) | |
b = 1 | b = 1 | b = 1 | ||||
a = 1 | a = 1 | y = a | ||||
x = b | y = a | a = 1 | ||||
y = a | x = b | x = b | ||||
The results of | a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=1 |
a=1 x=1 |
b=1 y=0 |
If x =0 and y =0 exist at the same time, the program will have no result.
But run the program:
Number of program runs:5130Program time:2453
Copy the code
The program exits successfully, indicating that xy is zero at the same time. This means that the instructions of a thread are not executed in order and the order is out of order.