Most Java programmers have learned the use of the volatile keyword. The definition of volatile is as follows:
Volatile is a type specifier designed to modify variables that are accessed and modified by different threads. 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.
There may be a lot of friends who just learned Java after reading the above very general description still feel confused.
Let’s use a specific example to learn how to use volatile.
Look at this example:
public class ThreadVerify {
public static Boolean stop = false;
public static void main(String args[]) throws InterruptedException {
Thread testThread = new Thread(){
@Override
public void run(a){
int i = 1;
while(! stop){//System.out.println("in thread: " + Thread.currentThread() + " i: " + i);
i++;
}
System.out.println("Thread stop i="+ i); }}; testThread.start(); Thread.sleep(1000);
stop = true;
System.out.println("now, in main thread stop is: "+ stop); testThread.join(); }}Copy the code
This code defines a stop variable on the second line of the main thread. The main thread then starts a new thread, incrementing the counter I in the thread until the main thread sets stop to true.
The main Thread pauses with thread. sleep for 1 second and sets the Boolean stop to true.
Therefore, we expect the above Java code to stop after one second and print out the actual value of the counter I for one second.
However, after executing the Java application, you find that it has gone into an infinite loop and that the CPU usage of the Java application has skyrocketed in the task manager.
The reason? Let’s go over the memory model that we talked about in the operating system class.
Taking the Java memory model as an example, the Java memory model is divided into main memory and work memory. Variables in main memory are shared by all threads. Each thread has its own working memory, which contains thread-local variables. The thread’s working memory maintains a copy of the main memory variable if it is used by the thread.
All reads and writes to variables by a thread must be done in working memory, not in main memory. Threads cannot directly access each other’s working memory. Variable transfer between threads is done through main memory. The interaction among thread, main memory, and working memory is shown as follows:
If a thread changes a variable defined in the main thread (main memory) in its own execution code, the change occurs directly in the thread’s working memory, and at some point (which the Java programmer has no control over, but is scheduled by the JVM), the change is written from working memory back to main memory.
Let’s go back to our example. Although the main thread changes the stop variable, it only changes the value in main memory, while the stop variable in the working memory of the thread that operates on the counter remains the old value, always false. So the thread is stuck in an infinite loop.
Once you know the principle, the solution is simple. The stop variable is preceded by the keyword volatile, so that each time a stop value is read in the counter thread, volatile forces that thread to read from main memory rather than from the current thread’s working memory. This avoids an endless loop. The figure below shows that after one second, the counter had executed 1.4 billion times.
For more of Jerry’s original technical articles, please follow the public account “Wang Zixi” or scan the following QR code: