Foreword:
There are many ways to implement singleton mode, such as hungry mode, lazy mode, static inner class and enumerations, etc., when the interviewer asks "Why must volatile be added to the singleton mode?", then he is referring to why the private variables in the lazy mode must be added volatile?
Lazy mode refers to the lazy loading method of object creation. The object is not created when the program starts, but is created only when it is actually used for the first time.
Would you like to explain why you need to add volatile? Let’s first look at the specific implementation code of lazy mode:
public class Singleton { // 1.防止外部直接 new 对象破坏单例模式 private Singleton() {} // 2.通过私有变量保存单例对象【添加了 volatile 修饰】 private static volatile Singleton instance = null; // 3.提供公共获取单例对象的方法 public static Singleton getInstance() { if (instance == null) { // 第 1 次效验 synchronized (Singleton.class) { if (instance == null) { // 第 2 次效验 instance = new Singleton(); } } } return instance; } }
As can be seen from the above code, in order to ensure thread safety and high performance, if and synchronized are used twice in the code to ensure the execution of the program. . So since there is already synchronized to ensure thread safety, why do we need to add volatile to the variable? Before explaining this problem, we must first understand a prerequisite knowledge: What is the use of volatile?
volatile has two main functions. First, it solves the problem of memory visibility. Second, it prevents instruction reordering.
The so-called memory visibility problem refers to when multiple threads operate a variable at the same time. After one thread modifies the value of the variable, other threads Modifications of variables cannot be perceived, which is a memory visibility problem. Using volatile can solve the memory visibility problem, such as the following code, when volatile is not added, its implementation is as follows:
private static boolean flag = false; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { // 如果 flag 变量为 true 就终止执行 while (!flag) { } System.out.println("终止执行"); } }); t1.start(); // 1s 之后将 flag 变量的值修改为 true Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("设置 flag 变量的值为 true!"); flag = true; } }); t2.start(); }
The execution results of the above program As follows:
However, after the above program has been executed for N times, it still has not ended. This shows that after thread 2 modified the flag variable, thread 1 did not Variable modifications are perceived.
Then next, we try to add volatile to the flag. The implementation code is as follows:
public class volatileTest { private static volatile boolean flag = false; public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { // 如果 flag 变量为 true 就终止执行 while (!flag) { } System.out.println("终止执行"); } }); t1.start(); // 1s 之后将 flag 变量的值修改为 true Thread t2 = new Thread(new Runnable() { @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("设置 flag 变量的值为 true!"); flag = true; } }); t2.start(); } }
The execution results of the above program are as follows:
From the above execution results, we can see that using volatile can solve the memory visibility problem in the program.
Instruction reordering means that during program execution, the compiler or JVM often reorders instructions to improve the execution performance of the program. The original design intention of instruction reordering is indeed very good, and it can also play a great role in a single thread. However, in multi-threads, using instruction reordering may cause thread safety issues.
The so-called thread safety problem refers to the execution result of the program that does not match our expectations. For example, if the correct result we expected is 0, but the execution result of the program is 1, then this is a thread safety issue.
Using volatile can prohibit instruction reordering, thereby ensuring that the program can be executed correctly when running in multiple threads.
Back to the topic, we use volatile in singleton mode, mainly because volatile can prohibit instruction reordering, thereby ensuring the normal operation of the program . Some readers may ask questions here, isn't synchronized already used to ensure thread safety? So why add volatile? Look at the following code:
public class Singleton { private Singleton() {} // 使用 volatile 禁止指令重排序 private static volatile Singleton instance = null; public static Singleton getInstance() { if (instance == null) { // ① synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); // ② } } } return instance; } }
Pay attention to the above code. I marked the two lines of code at ① and ②. Adding volatile to private variables is mainly to prevent the reordering of instructions during the execution of ②, that is, the execution of "instance = new Singleton()". This line of code seems to be just a process of creating an object, but it The actual execution is divided into the following 3 steps:
Create memory space.
#Initialize the object Singleton in the memory space.
#Assign the memory address to the instance object (after executing this step, instance will not be equal to null).
Just imagine, If volatile is not added, then thread 1 may execute the instruction reordering when executing the ② of the above code, changing the original The execution order of 1, 2, and 3 is rearranged to 1, 3, and 2. But in special circumstances, after thread 1 has executed step 3, if thread 2 comes and executes the first step of the above code, it is judged that the instance object is not null, but thread 1 has not yet finished instantiating the object. Then Thread 2 will get a "half" object that is instantiated, causing a program execution error. This is why volatile is added to the private variable.
The above is the detailed content of Why does the volatile keyword need to be added to Java singleton mode?. For more information, please follow other related articles on the PHP Chinese website!