I heard that wechat search “Java fish” really can be stronger oh!
Reordering and memory barriers are often mentioned when asked about multithreaded programming. If you have some understanding of both, that’s a plus.
(a) What is instruction reorder
In order to make the processor within the computing unit can be make full use of as far as possible, the processor may out-of-order execution of input code are optimized, the processor can be carried after calculating the out-of-order restructuring as a result, and ensure that the results and sequential results is consistent, but the process does not guarantee each statement calculation order and the order of the input code. This is instruction reorder.
Simply put, it means that the code you write in a program is not always executed in the order it was written.
In Java, the JVM is able to reorder machine instructions appropriately based on processor characteristics (CPU multi-level caching systems, multi-core processors, and so on) to maximize machine performance.
Instructions are reordered twice in Java, first at the stage of compiling bytecode into machine code, and second at CPU execution, where instructions are reordered as appropriate.
(2) Reordering of repetition instructions
It’s not easy to see what’s going on, but let’s take a look at a piece of code that’s been around a few times on the web, but it does reproduce reordering. I’ll put the explanation after the code.
public class VolatileReOrderSample {
// Define four static variables
private static int x=0,y=0;
private static int a=0,b=0;
public static void main(String[] args) throws InterruptedException {
int i=0;
while (true){
i++;
x=0; y=0; a=0; b=0;
// open two threads, the first thread executes a=1; x=b; The second thread executes b=1; y=a
Thread thread1=new Thread(new Runnable() {
@Override
public void run(a) {
// Thread 1 executes before thread 2, so nanoTime makes thread 1 wait 0.01 milliseconds for thread 2
shortWait(10000);
a=1; x=b; }}); Thread thread2=new Thread(new Runnable() {
@Override
public void run(a) {
b=1; y=a; }}); thread1.start(); thread2.start(); thread1.join(); thread2.join();// Wait for both threads to complete the result of the concatenation
String result="The first"+i+"Execute x="+x+"y="+y;
// If x=0 and y=0, the loop is broken
if (x==0&&y==0){
System.out.println(result);
break;
}else{ System.out.println(result); }}}// Wait for interval nanoseconds
private static void shortWait(long interval) {
long start=System.nanoTime();
long end;
do {
end=System.nanoTime();
}while(start+interval>=end); }}Copy the code
As long as this code looks, it’s actually quite simple. Define four static variables x,y,a, and b, set them all equal to 0 each time through the loop, and then use two threads. The first thread executes a=1; x=b; The second thread executes b=1; Y = a.
What are the results of this procedure? Logically, there should be three outcomes:
When the first thread executes to a=1, the second thread executes to b=1, and finally x=1, y=1;
When the first thread completes, the second thread has just started, and finally x=0, y=1;
When the second thread completes, the first thread starts, and finally x=1, y=0;
It’s theoretically impossible anyway that x=0,y=0;
But tens of thousands of times after the program is executed, the result of zero appears unexpectedly:
This is because the instructions are reordered so that x=b is executed before a=1 and y=a before b=1.
(c) by what means to forbid instruction reordering?
Volatile prevents instruction reordering through memory barriers, which are CPU instructions that guarantee the order in which a particular operation is executed.
There are four types of memory barriers:
StoreStore barrier, StoreLoad barrier, LoadLoad barrier, LoadStore barrier.
The JMM rules for Volatile reordering for compilers:
These theories may not be easy to understand, but let me explain them in popular terms:
The first is an understanding of the four memory barriers: Store is a write barrier and Load is a read barrier.
Let’s say I have two lines of code, a=1; X = 2; And I’m going to make x volatile.
When a=1 is executed, it is equivalent to a normal write operation.
At x=2, it is a volatile write;
Therefore, between these two lines of commands, a StoreStore barrier is inserted (write at the front and write at the back), which is the memory barrier.
If the first operation is a normal write and the second is a volatile write, then the corresponding value in the table is NO and reordering is prohibited. This is how Volatile does instruction reordering.
Now all we need to do is volatile the x and y of the code above, and the instruction reordering won’t happen (if you can push the logic through the table, you’ll get the idea).