Unique object creation -- singleton pattern

The instantiation of a class in Java is achieved through the new keyword. The goal and function of the singleton mode is to ensure that the class objects obtained at any time are the same. That is to say, you can only use the new keyword to create an object once, and keep this object for global use by the program. According to different usage scenarios, there are three different implementations of " singleton mode " , which are explained below:

 

Singleton pattern under single thread

 

The singleton pattern in a single thread is the most common way to use it, but it is often mistakenly used in the case of multiple threads. Its implementation is very simple:

In order to restrict the external use of the new keyword, the constructor must be set to private ;

In order to keep this created object, you must use a static constant to point to this object;

In order to use this object externally, a public static method must be exposed to obtain this object.

To meet these three conditions, the implementation code is as follows:

/**
 * Non-thread safe singleton mode
 * Created by gantianxing on 2017/10/17.
 */
public class Singleton0 {
 
    private static Singleton0 singleton0= null;
 
    private Singleton0(){
 
    }
 
    public static Singleton0 getInstance(){
        if(singleton0 == null){//In the case of multi-threading, it will be created multiple times
            singleton0 = new Singleton0();
        }
        return singleton0;
    }
}

In the case of a single thread, it is recommended to use this method. Some people will ask whether the current program is basically multi-threaded or not. In fact, the process of program startup initialization can be understood as a single thread. For example, in the method modified by @PostConstruct , the Singleton0.getInstance () method is first called to ensure that the singleton0 is only initialized once, and subsequent calls to Singleton0.getInstance () by multiple threads will not There is a new object creation, in fact, this scenario can also use the second way:

 

Static initialization singleton pattern

 

In a multi-threaded environment, using the first method, there will be multiple executions of singleton0 = new Singleton0();

This code creates the object, breaking the definition of the singleton pattern. In other words, the first method above cannot be used if it is a singleton mode in a single-threaded environment, and if it is a non-singleton mode in a multi-threaded environment. Let's implement the second singleton mode, which is the singleton mode implemented by static variable initialization:

 

/**
 * Static initialization thread-safe singleton
 * Created by gantianxing on 2017/10/17.
 */
public class Singleton1 {
    private static final Singleton1 singleton1 = new Singleton1();
 
    private Singleton1(){
 
    }
 
    public static Singleton1 getInstance(){
        return  singleton1;
    }
}
 

 

This method does not have the thread safety problem of the first initialization. The first initialization has been completed when the program is started, and the subsequent multi-threading will obtain a unified instance every time. But if the initialization time is long, it may affect the program startup. If the initialization time is not long, it is recommended to use this method, otherwise the third method is used for delayed initialization.

 

Double Checked Locking (DCL) Singleton Pattern

 

In fact, the first method is the delayed initialization method, which is initialized when it is used for the first time, but cannot be guaranteed to be initialized only once in the case of multi-threading. The easiest way to ensure synchronization is to add synchronized directly before the getInstance ( ) method , but there is unnecessary performance overhead in a multi-threaded environment . The specific implementation method:

public class Singleton2 {
 
    //Note that it must be volatile modification to ensure the visibility of data under multi-threading
    private volatile static Singleton2 singleton2 = null;
 
    private Singleton2(){
 
    }
 
    public static Singleton2 getInstance(){
        if(singleton2 == null){//First check
            synchronized(Singleton2.class){//Class synchronization
                if(singleton2 == null){//Second check
                    singleton2 = new Singleton2();
                }
            }
        }
        return singleton2;
    }
}
 

 

Double check locking: the first step is the first check to check whether singleton2 is empty; the second step is synchronized if it is empty, the synchronization code block is executed; the third step is the second check because there may be multiple threads in the case of multiple threads Waiting for the lock, when the first thread is executed, other threads waiting for the lock will be executed in sequence, so the process must check for the second time. If singleton2 is not empty, it does not need to be created again.

 

In addition, in order to ensure the timely visibility of data between multiple threads, the member variable singleton2 must be modified with volatile .

 

This is the classic double-checked locking implementation. The synchronized locking used here, of course, the Lock new lock api can also be used . This implementation can delay instantiation of singleton objects in a multi-threaded environment.

 

Extend initialization placeholder class

 

 

Extended initialization placeholder class: It is a delayed initialization version of the second method, specifically using static variables of static inner classes to initialize. The principle is to use only one thread to load the inner class when accessing the static inner class for the first time, thus ensuring thread safety. The specific implementation is as follows:

public class Singleton4 {
 
   public class Singleton4 {
    private static class InnerHolder{
        public static Singleton4 singleton4 = new Singleton4();
    }
    private Singleton4(){

    }
    public static Singleton4 getInstance(){
        return InnerHolder.singleton4;
    }
}

This lazy-initialized singleton pattern is more elegant and performant than the third DCL approach. In a multithreaded environment, this method is recommended.

 

The four common implementations of singleton patterns in java and the usage scenarios are summarized here, which can be flexibly selected according to the specific conditions of the project.

 

Finally, the singleton pattern is not only used to create an ordinary object, but also can be used to create a singleton Map, List, etc.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=326720859&siteId=291194637