The role of the volatile keyword
To understand the role of the volatile keyword, we can look at the following concepts level by level.
Concepts related to the memory model
Java Memory Model (JMM)
The Java Virtual Machine specification attempts to define a Java Memory Model (JMM) that screens out differences in memory access across hardware and operating systems so that Java programs can achieve consistent memory access across platforms. Simply put, this is because the CPU can execute instructions much faster than it can access memory, so several layers of caching are added to the CPU to increase access speed.
There is a problem
To achieve better execution performance, the Java memory model does not restrict the execution engine from using the processor’s registers or telling the cache to speed up instruction execution, nor does it restrict the compiler from reordering instructions. Simply put, in the Java memory model, there are problems with cache consistency and instruction reordering.
For example
In Java, execute the following statement
i = 7;
Copy the code
Instead of writing the value 7 directly to main memory, the executing thread must assign the value of variable I to the cache line in its own worker thread and then write it to main memory.
Introduction to atomicity, visibility, and orderliness
So what guarantees does the Java language itself provide for atomicity, visibility, and orderliness?
atomic
In Java, variable reads and assignments to basic data are atomic operations, that is, they are uninterruptible and either performed or not performed. Look at the following code:
x = 7; //语句1
y = x; //语句2
x++; //语句3
x = x + 1; //语句4
Copy the code
Statement 1 assigns the value 7 directly to x, meaning that the thread executing this statement writes the value 7 directly to working memory. Statements 2 actually contains two operations, it should first to read the value of x, then the value of x to the working memory, while reading the value of x and to write the value of x into the working memory of these two operations are atomic operations, but taken together, it is not atomic operations, because in the process of access, other threads can cause the change of the variable. Statements 3 and 4 are the same as statements 2, i.e., only simple reads and assignments. As you can see from above, the Java memory model values guarantee that basic reads and assignments are atomic operations, which can be achieved by synchronized and Lock for larger operations.
##### Visibility For visibility, Java provides the volatile keyword to ensure visibility. Visibility is refers to multiple threads access the same variable, one thread to modify the value of the variable, other threads can immediately see the value of the modified When a Shared variable is decorated volatile, it would ensure that the modified value will immediately be updated in main memory, when he has other threads need to read, it will go to the new value read from the memory.
Visibility can also be guaranteed with synchronized and Lock, which flush shared variable values back to main memory before releasing the Lock, but synchronized and Lock are more expensive.
code
The following code explains what visibility means:
public class Main {
private static boolean flag = true;
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
System.out.println("Thread 1 has started executing.");
while (flag) {
}
System.out.println("Thread 1 completes execution.");
});
Thread thread2 = new Thread(() -> {
System.out.println("Thread 2 is executing.");
while(! flag) { } System.out.println("Thread 2 finished executing.");
});
thread1.start();
try {
Thread.sleep(1200);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = false; thread2.start(); }}Copy the code
When flag is set to true, thread 1 is still stuck in an infinite loop. When flag is set to false, thread 1 is still stuck in an infinite loop, indicating that thread 1 cannot see the changed value immediately. The running results are as follows:
thread1The thread starts executing2It's on.Copy the code
When the volatile modifier is added to flag, that is
private volatile static boolean flag = true;
Copy the code
The running result is as follows:
thread1The thread starts executing1Execution terminates the thread2It's on.Copy the code
When the main thread changes the value of flag, thread 1 immediately sees the changed value.
order
The JMM allows the compiler and processor to reorder instructions, using volatile keywords to disable reordering and ensure program “order.”
conclusion
The order of a volatile variable has two meanings:
- All writes that precede volatile variable writes are visible to statements that follow volatile variable reads.
- Disallow JVM reordering: The reads and writes of volatile variables cannot be reordered with any instructions surrounding them, which may be reordered