introduce

Singleton Pattern is a relatively simple design Pattern, which belongs to the creation Pattern. The definition for

Ensure that there is only one instance of a class, and that you instantiate it yourself and make it available to the entire system

In the system, the singleton mode requires that a singleton object can have only one instance. If there are multiple instances of this type of object, some problems may occur, such as excessive resource consumption and inconsistent processing results. Generally, the singleton can be used in the following scenarios

  • Generate unique sequence number
  • Shared access points or shared data, such as Web page counters, throughout the project
  • Creating an object instance consumes too many resources, such as I/O and database connections

In Java, only one instance of a singleton exists within a JVM. The following is the singleton schema structure diagram

  • There is a static member variable of the singleton
  • Private constructors that can only be instantiated by themselves
  • Provide a static public method to instantiate an object and access an instance of the object

Singleton pattern implementation

The singleton pattern can be implemented in two ways:

  • The hungry type
  • Lazy (lazy loading)

The hungry type

/** ** */
public class Singleton1 {
    // Static member variable that instantiates the object during static initialization
    private static final Singleton1 singleton = new Singleton1();
    // Construct private
    private Singleton1(a){}public static Singleton1 getSingletonInstance(a){
        returnsingleton; }}Copy the code

Refers to the hungry type, at the time of class loading and instantiate the singleton, regardless of use, to create the first say. This is thread-safe, but if the object is never used, space resources are wasted.

But for singletons that take up a lot of space, or are only used in certain scenarios, we want to instantiate them the first time we use them, which requires lazy lazy loading

LanHanShi

/** * lazy singleton (not thread-safe) */
public class Singleton2 {
    private static Singleton2 singleton;
    private Singleton2(a){}
    // Get the instance
    public static Singleton2 getSingletonInstance(a){
        if(singleton == null){
            singleton =  new Singleton2();
        }
        returnsingleton; }}Copy the code

As you can see from the code above, slacker and hunker styles depend on when singleton objects are created. The hanky-style instantiates the object when the class is loaded and returns the object directly without judgment. Lazy is instantiated on the first invocation, and each invocation needs to determine whether it has already been instantiated

However, this approach is not safe with multiple threads. When multiple threads access getSingletonInstance() at the same time, multiple instances may be created instead of singletons. So what about thread safety? The first thing you might think of is adding the synchronized keyword to the getSingletonInstance() method

/** * synchronized singleton */
public class Singleton3 {
    private static Singleton3 singleton;
    private Singleton3(a){}
    // Get the instance
    public static synchronized Singleton3 getSingletonInstance(a){
        if(singleton == null){
            singleton =  new Singleton3();
        }
        returnsingleton; }}Copy the code

The getSingletonInstance() method adds a synchronization lock, which increases the elapsed time of obtaining the instance and can block in multiple threads. But we don’t want to lock every time we get an instance, we just want to be thread-safe on the first call to create an object

Double check lock (DCL)

Locking the getSingletonInstance() method does ensure thread-safety, but there is a performance problem. Is it necessary to lock the whole method? Or do I synchronize when I check that the instance has not been created yet

** Double-checked locking (DCL) ** can solve this problem

/** * double-checked locking */
public class Singleton4 {
     The /** * member variable has the keyword volatile to prevent instruction reordering */
    private static volatile Singleton4 singleton;
    private Singleton4(a){}
    // Get the instance
    public static Singleton4 getSingletonInstance(a){
        // On the first check, the synchronized code block is entered without instantiation
        if(singleton == null) {synchronized (Singleton4.class){
                // Create an instance if it is empty
                if(singleton == null){
                    singleton =  newSingleton4(); }}}returnsingleton; }}Copy the code

Do not lock the method, only the code that creates the instance. The first is for unnecessary synchronization, and the second is to enter the synchronized code block and determine that the instance is null

Note that the member variable is volatile

Volatile ensures the visibility of data, but synchronized also ensures the visibility of synchronized data, and the main purpose of volatile is to prevent Java instruction reordering

Static inner class

/** * static inner class */
public class Singleton5 {
    private Singleton5(a){}

    // Get the instance
    public static Singleton5 getSingletonInstance(a){
        return SingletonHolder.SINGLETON;
    }

    /** * Internal classes. The JVM is mutually exclusive during class loading to ensure thread-safety */
    private static class SingletonHolder{
        private static final Singleton5 SINGLETON = newSingleton5(); }}Copy the code

The JVM ensures that data is synchronized during class loading, and we can create singleton objects from inner classes. The inner class is not loaded when Singleton5 is first loaded and is not loaded when the inner class is not used. Only the first call to getSingletonInstance() loads the inner class and instantiates the singleton, making it lazy and thread-safe

Enumeration methods

Using enumerations to implement singletons is very succinct, supports serialization mechanisms, and absolutely prevents multiple instantiations

/** * Enumeration mode */
public enum Singleton6 {

    /** * enumeration to implement singleton */
    SINGLETON;

    public void handle(a) {
        // to do something}}Copy the code

How to use this singleton

public class SingletonDemo {

    @Test
    public void test(a){
        // Enumeration modeSingleton6 singleton = Singleton6.SINGLETON; singleton.handle(); }}Copy the code