The singleton pattern is one of the simplest and most common design patterns among the 23. Many people learn design patterns and come into contact with the singleton pattern first.

The singleton pattern is the creation pattern, which aims to create only one instance in the current process, or possibly in a single thread.

Writing a singleton that is safe and concise can be potentially buggy without attention to detail. This article summarizes the four most common singleton styles and analyzes the pros and cons of each.

Lazy mode

Lazy mode, which is created only when used, is the idea of lazy loading.

public class Singleton { private static Singleton instance; private Singleton (){} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}Copy the code

This is obviously lazy, but fatal in a multi-threaded environment does not work well. Consider the concurrency safe lazy mode.

public class Singleton { private static Singleton instance; private Singleton (){} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; }}Copy the code

This works well in multithreading and seems to have good lazy loading, but synchronized locks every time it acquires a singleton, so it’s inefficient and doesn’t require synchronization 99% of the time. Take a look at the optimized version:

public class Singleton {  
    private volatile static Singleton singleton;  
    private Singleton () {}  
    public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
                if (singleton == null) {  
                    singleton = new Singleton();  
                }
            }
        }
        return singleton;  
    }  
}  
Copy the code

This is the famous double-checked locking mode, but some people doubt the reliability of this singleton mode. I will not explain too much here. Interested partners can go to the Internet to search: double-checked locking is broken.

Note that the Singleton instance variable must have the volatile keyword. Volatile, where the order property is used, prevents instruction reordering. Many bloggers claim to take advantage of volatile visibility, which is not true.

singleton = new Singleton();
Copy the code

It consists of three steps:

  1. Allocates memory for objects

  2. Instantiate an object

  3. Point the reference to the corresponding memory address

Instruction rearrangement may occur in steps 2,3, where the first line points the singleton to the memory address of an uninstantiated object before instantiating the object.

If the second thread makes the first non-null judgment, it will return the memory address of the object that has not been instantiated, which may cause a null-pointer exception.

The hungry mode

Hungry Man mode is where power is created before it is used. Its implementation code is as follows:

public class Singleton { private static Singleton instance = new Singleton(); private Singleton (){} public static Singleton getInstance() { return instance; }}Copy the code

This method is based on the classloder mechanism to avoid multithreading synchronization problems, instance is instantiated at the time of class loading, obviously does not achieve lazy loading effect, there are also variations.

public class Singleton { private Singleton instance = null; static { instance = new Singleton(); } private Singleton () {} public static Singleton getInstance() { return this.instance; }}Copy the code

Static inner class

This is one of the more common ways I write code.

public class Singleton { private static class SingletonHolder { private SingletonHolder() {} private static final Singleton INSTANCE = new Singleton(); public static final Singleton getInstance() { return INSTANCE; } } public static Singleton getSingleton() { return SingletonHolder.getInstance(); }}Copy the code

Or:

public class Singleton2 { private Singleton2() {} private static final class Singleton2Holder { public static final Singleton2 INSTANCE = new Singleton2(); } public static Singleton2 getInstance() { return Singleton2Holder.INSTANCE; }}Copy the code

This approach also uses the classloder mechanism to ensure that instance is initialized with only one thread,

The Singleton class is loaded, and the instance is not necessarily initialized.

The enumeration

Enumerations are similar in nature to static blocks of code in that constructors are called automatically with enumerations, and you can also use this feature to implement the singleton pattern, though less commonly.

public class EnumSingleton{ private EnumSingleton(){} public static EnumSingleton getInstance(){ return Singleton.INSTANCE.getInstance(); } private static enum Singleton{ INSTANCE; private EnumSingleton singleton; Private Singleton(){Singleton = new enumleton (); } public EnumSingleton getInstance(){ return singleton; } } } public static void main(String[] args) { EnumSingleton obj1 = EnumSingleton.getInstance(); EnumSingleton obj2 = EnumSingleton.getInstance(); System.out.println("obj1==obj2?" + (obj1==obj2)); // true }Copy the code

Conclusion:

Singleton mode mainly includes slacker mode, hungry mode, inner class, enumeration-based, and four writing methods. The inner class mode is relatively common and recommended.

The design pattern is simple to use. In many complex business scenarios, multiple patterns are often mixed, such as singleton pattern + factory pattern. And the important thing is to master, not to memorize, so that flexible application.

END