The role of Volatile in DCL (double lock check), how to prevent reflection from instantiating singleton objects

Why is there a DCL singleton mode? I believe that many of the singleton modes written by you are the conventional lazy mode without locking. In fact, this kind of writing has no effect on a single thread. Once it is used in multiple threads, multiple objects will be created. This is not a singleton mode. In order to be a singleton mode in multithreading, we introduced the DCL singleton mode.

Before introducing the DCL singleton mode, let us first understand the volatile keyword:

The variable modified by the Volatile modifier ensures that this instruction will not be omitted due to compiler optimization when the compiler compiles.

Main features:

  • Ensure real-time visibility when different threads modify the variable at the same time, that is, when one thread modifies the variable, other threads are visible in real time;
  • Reordering instructions is prohibited (especially important);

DCL singleton instruction flow:

  1. Allocate memory for the object;
  2. Initialize the instance object;
  3. Allocate memory for object references;

In order to optimize the instructions and improve the efficiency of program operation, the JVM allows instruction reordering. If the JVM optimizes instructions to execute 1, 3 , and 2 sequentially, then when multiple threads execute tasks at the same time, if instruction 3 is just executed, instruction 2 will be executed in the future, and others When the thread calls getInstance() to execute the task, it will throw an object uninitialized exception ; so in order to solve this problem, we quote the Volatile keyword to modify the variable.

public class SingleDemo {
    private static volatile SingleDemo instance;

    private SingleDemo() {
    }

    public static SingleDemo getInstance() {
        if (instance == null) {//不用volatile修饰的话,线程二提前访问指令顺序被打乱,认为不为空跳过判断
            synchronized (SingleDemo.class) {
                if (instance == null) {
                    instance = new SingleDemo();//不用volatile修饰的话,线程一指令顺序被打乱未来及创建对象
                }
            }
        }
        return instance;//不用volatile修饰的话,线程二直接拿到instance
    }
}

After reading the above code, it is estimated that some students will ask why the synchronization lock should be written in the method instead of directly on the external method name, like this:

    public static synchronized SingleDemo getInstance() {
        if (instance == null) {
            instance = new SingleDemo();
        }
        return instance;
    }

Although this ensures multi-thread safety, it will cause a lot of performance overhead. Locking only needs to be used during the first initialization, and subsequent calls do not need to be locked, so in order to solve this problem DCL ( Double lock check) singleton mode was born. Of course, the synchronization lock can also prevent reflection from destroying the instantiated singleton object;

Some students may ask why you don’t need synchronized (this) for a synchronized lock, like this:

    public static SingleDemo getInstance() {
        if (instance == null) {//不用volatile修饰的话,线程二提前访问指令顺序被打乱,认为不为空跳过判断
            synchronized (this) {
                if (instance == null) {
                    instance = new SingleDemo();//不用volatile修饰的话,线程一指令顺序被打乱未来及创建对象
                }
            }
        }
        return instance;//不用volatile修饰的话,线程二直接拿到instance
    }

This involves the type of lock: method lock, object lock, class lock:

  1. The method lock is, for example, the public static synchronized SingleDemo getInstance() of the synchronized modification method. The function is that the method can only be passed when multiple threads call the method at the same time, and the scope is when all objects call the method;
  2. The object lock is, for example, synchronized (this). The function is that when multiple threads call the object lock at the same time, only one can be passed. If multiple objects are responsible for failure, the scope value is in this object;
  3. The class lock is, for example, synchronized (SingleDemo.class). The function is that only one object can be created by multiple threads at the same time. The scope is that when the class only exists to create the object, the object is invalidated when it is called;

Therefore, the above method of using object locks is incorrect. Here is to create objects by instantiation, and class locks should be used.

Guess you like

Origin blog.csdn.net/xhf_123/article/details/108468393