The lazy singleton pattern from shallow to deep

1. The most basic version, does not consider thread safety issues

package JavaBasis.chapter8;

public class Singleton {
    
    
    private static Singleton instance=null;
    private Singleton(){
    
    }//私有化构造函数,外部不可见
    private static Singleton getInstance()
    {
    
    
           if(instance==null)//实例未被创建
           instance=new Singleton();

           return instance;//返回创建的实例
    }
}

2. The introduction of multi-threaded problems

The above code has thread insecurity problems in a multithreaded environment. For example, thread 1 executes to the if judgment statement first, and finds that the instance has not been created, so it starts to create the instance; at this time, thread 2 comes again, but the instance of thread 1 has not yet After creation, the if judgment is still satisfied, and then thread 2 enters the creation statement, resulting in two instace objects at the end

  • Solution 1 Add a lock to the entire method
package JavaBasis.chapter8;

public class Singleton {
    
    
    private static Singleton instance=null;
    private Singleton(){
    
    }//私有化构造函数,外部不可见
    private static synchronized Singleton getInstance()
    {
    
    
           if(instance==null)//实例未被创建
           instance=new Singleton();

           return instance;//返回创建的实例
    }
}

This solution can solve the thread safety problem, but there is another problem. Because the entire method is locked, suppose I now have a thread 1 that gets the lock and enters the method to create the object. Thread 1 ends, and then Thread 2 comes and the method finds that the object has been created and returns directly, but if thread 3 also comes at this time, because the entire method is locked, thread 3 cannot enter. It must wait for thread 2 to end before it can enter, but it Just want to return an object that has been created, wasting time and incurring performance overhead, and then you may come up with the following solution

  • Solution 2 Add synchronized to the creation process
package JavaBasis.chapter8;

public class Singleton {
    
    
    private static Singleton instance=null;
    private Singleton(){
    
    }//私有化构造函数,外部不可见
    private  static  Singleton getInstance()
    {
    
           if(instance==null)//实例未被创建
           {
    
    
           synchronized(Singleton.class)
           {
    
     instance=new Singleton();
           }
           }

           return instance;//返回创建的实例
    }
}

The above code also has thread safety issues. Now thread 1 enters the method first and gets the lock to start creating the object. Before the thread 1 object is created, thread 2 also comes in. Thread 2 judges if it finds that the object has not been created, so Enter, but can't get the lock, so it starts to wait. After thread 1 creates the object and releases the lock, thread 2 creates the object again, because it has already performed an if judgment before

  • Solution 3 Double inspection lock
package JavaBasis.chapter8;

public class Singleton {
    
    
    private static Singleton instance=null;
    private Singleton(){
    
    }//私有化构造函数,外部不可见
    private  static  Singleton getInstance()
    {
    
           if(instance==null)//实例未被创建
           {
    
    
           synchronized(Singleton.class)
           {
    
     
           if(instance==null)
           instance=new Singleton();
           }
           }

           return instance;//返回创建的实例
    }
}

The above problem can be solved by adding a judgment to the problem of scheme 2, such as the thread 2 mentioned in scheme 2, although it has passed the first if judgment, when it gets the lock, it will perform the if judgment. When it is found that the object has been created by thread 1, so it is directly returned to the object, so far, do you think you are done, but even if it is a double check lock, there are certain problems

      if(instance==null)//实例未被创建
           {
    
    
           synchronized(Singleton.class)
           {
    
     
           if(instance==null)
           instance=new Singleton();//1
           }
           }

Looking at this code, the process of creating an object is divided into the following three steps:
1. Allocate the memory space of the
object 2. Initialize the object
3. Let the instance reference point to the address of the initialized object,
but instruction rearrangement occurs in Java Specifically, the order of 2 and 3 will be changed. It may execute 3 and then execute 2. What problems will this cause?
For example: thread 1 enters the synchronized code block and starts to create objects, executes 1, and then executes 3. After the execution of 3instance is not nul, instance points to a clear address, but the object in this address is not initialized. When thread 1 has not had time to perform operation 2, thread 2 comes in, and it finds the first if statement (or maybe It's the second if statement) The instance inside is not null, so the instance is
returned directly, but the instance is an uninitialized object. Solution: Add the volitile keyword in front of the instance, and the instruction rearrangement will be closed.

Guess you like

Origin blog.csdn.net/qq_43478694/article/details/114966158