Five implementations of singleton mode

Singleton mode

The singleton pattern, as its name implies, ensures that a class has only one instance and provides a global access point.

Five implementation methods:

  • Hungry Han style
  • Lazy style
  • Double check
  • Static inner class
  • Enumeration class

The first step in implementing the singleton pattern is to privatize the constructor to ensure that instances can only be created inside the class.

class Singleton{
    private static Singleton instance;
    private Singleton{
        
    }
    static Singleton getSingleton()
    {
        return instance;
    }
}

static is used to decorate instance to ensure that there is only one instance in memory.

static is also used to provide an access point for external users to access internal properties and methods of the class without using the new method (of course, new objects from Singleton are no longer supported because of the privatization of the structure).

The singleton pattern is divided into several types according to how to create instances in the class.

Hungry Han style

"A hungry man put food in his mouth when he saw what he could eat“

That is to create an instance to solve the requirements. There is no thread safety problem.

public class Singleton{
    private static Singleton instance=new Singleton();
    private Singleton{
        
    }
    //Provide a static method
    public static Singleton getSingleton()
    {
        return instance;
    }
}
class SingletonTest
{
    public static void main(String [] args)
    {
        Singleton s1=Singleton.getSingleton();
        Singleton s2=Singleton.getSingleton();
    }
}

Of course, although hungry Han style creates s1 and s2, they all point to the same heap memory (the function of static).

Lazy style

Lazy type does not create instances as hungry type, but wait until needed

public class Singleton{
    private static Singleton instance;
    private Singleton{
        
    }
    //Provide a static method externally, and create an instance only when it is used
    public static synchronized Singleton getSingleton()
    {
        if(instance==null)
        {
            instance=new Singleton();
        }
        return instance;
    }
}
class SingletonTest
{
    public static void main(String [] args)
    {
        Singleton s=Singleton.getSingleton();
    }
}

The lazy type has thread safety problems. If two threads Thread1 and Thread2 use getSingleton, Thread1 and Thread2 to reach the if judgment statement at the same time, two instances will be created, resulting in thread insecurity. Therefore, getSingleton can be locked online, but it will cause low efficiency.

Double check

Double check is a further improvement in the hungry Han style, which puts the thread lock inside to ensure the safety of the thread and improve the efficiency at the same time.

public class Singleton {
 
    //jvm class loading mechanism, static variable loading method area, thread public
    //volatile keyword to ensure visibility and order. Write operations occur first and then read operations facing this variable
    private volatile static Singleton instance;
 
    private Singleton() {
    }
 
    //Initialize only once
    public static Singleton getInstance() {
        if (instance == null) {
            //Multi thread concurrency solution to ensure atomicity
            synchronized (Singleton.class) {//1
                if (instance == null) {
                    instance = new Singleton();
                }
            }//2
        }
        return instance;
    }
 
}
 

Thread A and thread B enter the first if statement at the same time. Thread A obtains the thread lock first, and thread B blocks at 1. Since instance is null at this time, thread A creates an instance,

When you go to 2, unlock and thread B enters. Since instance is not empty, no more instance will be created. In theory, double check ensures thread safety.

But the problem with double check locking is that there is no guarantee that it will run smoothly on single processor or multiprocessor computers.

The problem of double check locking failure is not attributed to the implementation bug in the JVM, but to the Java platform memory model. The memory model allows so-called "unordered writes", which is a major reason why these idioms fail.

instance = new Singleton();

This statement is a non atomic operation, which is actually three steps.

  1. Allocate memory to instance;
  2. Call the constructor of instance to initialize the member variable;
  3. Point the instance object to the allocated memory space (singleton is not null at this time);

Virtual machine instruction reordering: when executing the command, the virtual machine may exchange positions for the above three steps. Finally, it may not be initialized after allocating memory and modifying the pointer. There may be problems in multi-threaded acquisition.

When thread A enters the synchronization method, execute instance = new Singleton(); The code is 132 after the reordering of these three steps,

The first thread initializes the object halfway, and the second thread returns it directly when it finds that it is not null. In fact, the object has not been fully initialized at this time. At this time, thread B executes the getInstance() method. In the first step, if the singleton is not null, it directly returns the incompletely initialized singleton object.

If a field is declared volatile, the Java thread memory model ensures that all threads see the same value of this variable and prohibits instruction reordering

Therefore, using volatile keyword will prohibit instruction reordering, which can avoid this problem. After using volatile keyword, make instance = new Singleton(); The statement must be executed according to step 123 split above.

Static inner class

public class Singleton {
    /**
     * Static inner class
     **/
    private static class SingletonClassInstance{
        private static final Singleton instance = new SingletonDemo4();
    }

    private Singleton(){}

    public static Singleton getInstance(){
        return SingletonClassInstance.instance;
    }
}

Enumeration class

public enum Singleton{

    //The enumeration element itself is a singleton
    INSTANCE;

    //Add your own operations directly through singleton INSTANCE. Just call dosomething(). Convenient, concise and safe.
    public void doSomething() {
        System.out.println("doSomething");
    }
}

class Test {
    public static void main(String[] args) {
        Singleton.INSTANCE.doSomething();
    }
}

Static inner classes can see this: https://blog.csdn.net/mnb65482/article/details/80458571

Keywords: Design Pattern Singleton pattern

Added by emediastudios on Sun, 26 Dec 2021 05:21:28 +0200