101 singleton mode (creation type)

Posted by smartknightinc on Sat, 29 Jan 2022 00:59:42 +0100

1. Single case introduction

Definition: Ensure a class has only one instance, and provide a global point of access to it

(ensure that a class has only one instance, and instantiate it by itself and provide this instance to the whole system.)

Concept: an object can only create one instance

Follow the rule: privatize the constructor and obtain the unique instance of this class through other methods

main points:

① Only one instance of a class can be privatized by the constructor

② You must create this instance yourself and include a static variable of this class to save this unique instance

③ You must provide this instance to the whole system yourself

Provide external ways to obtain the instance object: ① direct exposure; ② obtain it with the get method of static variables

Several of singleton mode

1. Han formula (static constant)

2. Hungry Chinese style (static code block)

3 lazy (thread unsafe)

4 lazy (thread safety, synchronization method, synchronization code block, synchronization lock)

5 double check

6 static inner class

7 enumeration

2 example code

2.1 hungry Han style

2.1.1 hungry Han formula - static constant

Hungry Chinese style: according to the jvm virtual machine: the statically decorated will only be loaded once to achieve the single instance effect

class SingletonHungry1 {//Hungry Chinese static constant
    /*
    /The advantages are simple and avoid the synchronization problem of multithreading
    Disadvantages: it does not achieve the effect of lazy loading, and the waste of memory
     */
    private SingletonHungry1(){
        System.out.println("SingletonHungry1 Static constant construction method");
    }
    private final static SingletonHungry1 INSTANCE = new SingletonHungry1();

    public static SingletonHungry1 getInstance(){
        return instance;
    }
}
//Whether the test meets the single example
   public static void main(String[] args) {
        SingletonHungry2 singletonHungry1 = SingletonHungry2.getInstance();
        SingletonHungry2 singleton2 = SingletonHungry2.getInstance();
        System.out.println(singletonHungry1==singleton2);
    }

2.1.2 hungry Chinese - static code block

class SingletonHungry2 {//Starving static code block
    /*
    /The advantages are simple and avoid the synchronization problem of multithreading
    Disadvantages: it does not achieve the effect of lazy loading, and the waste of memory
     */
    static {
        instance = new SingletonHungry2();
    }
    private static SingletonHungry2 instance =null;
    private SingletonHungry2(){
        System.out.println("SingletonHungry2 Static constant construction method");
    }

    public static SingletonHungry2 getInstance(){
        return instance;
    }
}

Advantages: this method is relatively simple, that is, the instantiation is completed when the class is loaded. Thread synchronization problems are avoided.

Disadvantages: the instantiation is completed when the class is loaded, which does not achieve the effect of Lazy Loading. If you have never used this instance from beginning to end, it will cause a waste of memory

This method is based on the classloder mechanism and avoids the problem of multi-threaded synchronization. However, instance is instantiated during class loading. In the singleton mode, most of them call the getInstance method, but there are many reasons for class loading. Therefore, it is uncertain that other methods (or other static methods) lead to class loading, At this time, initializing instance does not achieve the effect of lazy loading

Conclusion: This singleton mode is available, which may cause a waste of memory

2.1.3 enumeration (recommended) hungry Chinese style

public enum EnumSingle {
    INSTANCE; //attribute
    public void sayOK() {
        System.out.println("ok~");
    }
}

public class SingletonTest {
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                EnumSingle enumSingle1 = EnumSingle.INSTANCE;
                EnumSingle enumSingle2 = EnumSingle.INSTANCE;
                System.out.println(enumSingle1==enumSingle2);
            },String.valueOf(i)).start();
        }
    }
}

With jdk1 5 to implement the singleton mode. It can not only avoid the problem of multi-threaded synchronization, but also prevent deserialization and re creation of new objects.

This approach is advocated by Josh Bloch, author of Effective Java

Conclusion: it is recommended to use

2.2 lazy style

2.2.1 lazy (thread unsafe)

Not recommended

class SingletonLazy1 {//Lazy threads are unsafe
    /*
   Advantages: it has the effect of lazy loading and will not cause memory waste
    Disadvantages: thread unsafe. This method is not recommended
     */
    private SingletonLazy1(){
        System.out.println("SingletonLazy1 Lazy construction method");
    }
    private static SingletonLazy1 instance = null;

    public static SingletonLazy1 getInstance(){
        if (instance==null){
            instance = new SingletonLazy1();
        }
        return instance;
    }
}
public class SingletonLazyTest {//In the case of concurrency, the singleton effect is not satisfied
    public static void main(String[] args) {
        for (int i = 0; i < 10; i++) {
            new Thread(()->{
                SingletonLazy1 singletonLazy1 = SingletonLazy1.getInstance();
                System.out.println(singletonLazy1);
            },String.valueOf(i)).start();
        }
    }
}

View the running results. In the case of concurrency, multiple objects will be created and the construction method will be executed multiple times

It has the effect of Lazy Loading, but it can only be used under single thread.

If a thread enters the if (singleton == null) judgment statement block and has not had time to execute, another thread also passes the judgment statement, multiple instances will be generated. Therefore, this method cannot be used in a multithreaded environment

Conclusion: do not use this method in actual development

2.2.2 lazy (thread safety, synchronization method, synchronization code block, synchronization lock)

Not recommended

class SingletonLazy2 {//Three ways of lazy thread safety synchronization lock
    /*
    The thread safety problem is solved, but the efficiency is too low
     */
    private static SingletonLazy2 instance = null;
    private static ReentrantLock lock = new ReentrantLock();
    private SingletonLazy2(){
        System.out.println("SingletonLazy2 Lazy construction method");
    }

 /*   public static synchronized SingletonLazy2 getInstance(){
        if (instance==null){
            instance = new SingletonLazy2();
        }
        return instance;
    }*/
 /*   public static SingletonLazy2 getInstance(){
        synchronized(SingletonLazy2.class){
            if (instance==null){
                instance = new SingletonLazy2();
            }
            return instance;
        }
    }*/
    public static  SingletonLazy2 getInstance(){
        try{
            lock.lock();
            if (instance==null){
                instance = new SingletonLazy2();
            }
            return instance;
        }finally {
            lock.unlock();
        }
    }
}

The thread unsafe problem is solved

The efficiency is too low. When each thread wants to obtain an instance of a class, it needs to synchronize the getInstance() method. In fact, this method only executes the instantiation code once. If you want to obtain this kind of instance later, just return it directly. Method is too inefficient to synchronize

Conclusion: this method is not recommended in practical development

2.3 double check (recommended)

class SingletonLazy4 {//Double check DCL
    /*
    volatile must be added
    The possibility of instruction rearrangement is very small
     */
     // private static SingletonLazy4 instance = null;
    private static volatile SingletonLazy4 instance = null;
    private SingletonLazy4(){
        System.out.println("SingletonLazy3 Lazy construction method");
    }
   
    public static SingletonLazy4 getInstance(){
        if (instance==null){
            synchronized (SingletonLazy4.class){
                if (instance==null){
                    instance = new SingletonLazy4();
                }
            }
        }
        return instance;
    }
}
public class SingletonTest {
    public static void main(String[] args) {
        for (int i = 0; i < 100000; i++) {
            new Thread(()->{
                DoubleDCLSingle doubleDCLSingle = DoubleDCLSingle.getInstance();
                System.out.println(doubleDCLSingle);
            },String.valueOf(i)).start();
        }
    }
}

The concept of double check is often used in multithreading development. As shown in the code, we have conducted two if (singleton == null) checks to ensure thread safety.

In this way, the instantiated code is executed only once. When accessing again later, judge if (singleton == null) and return the instantiated object directly, which also avoids repeated method synchronization

Thread safety; Delayed loading; High efficiency

Conclusion: this single case design mode is recommended in practical development

2.2.4 static internal class (recommended)

package com.design.singleton;

/**
 * @author nzy
 * @create 2021-12-14 16:15
 * Using static inner classes is recommended
 * Using jvm to help us ensure thread safety
 */
public class StaticInnerSingle {
    //Constructor privatization
    private StaticInnerSingle() {
        System.out.println("StaticInnerSingle Construction method");
    }
    //Write a static inner class with a static attribute Singleton
    private static class SingletonInstance {
        public static final StaticInnerSingle INSTANCE = new StaticInnerSingle();
    }
    // Provide a static public method and directly return singletoninstance INSTANCE
    public static StaticInnerSingle getInstance() {
        return SingletonInstance.INSTANCE;
    }
}
public class SingletonTest {
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()->{
                StaticInnerSingle staticInnerSingle = StaticInnerSingle.getInstance();
                System.out.println(staticInnerSingle);
            },String.valueOf(i)).start();
        }
    }
}

This method adopts the mechanism of class loading to ensure that there is only one thread when initializing the instance.

The static internal class method does not instantiate the Singleton class immediately when it is loaded. Instead, when instantiation is needed, call the getInstance method to load the Singleton class, so as to complete the instantiation of Singleton.

The static properties of the class will only be initialized when the class is loaded for the first time, so here, the JVM helps us ensure the safety of threads. When the class is initialized, other threads cannot enter.

Advantages: thread insecurity is avoided, delayed loading is realized by using the characteristics of static internal classes, and the efficiency is high

Conclusion: it is recommended to use

3 Summary

According to the time when the instance object is created, the singleton mode can be divided into two categories. If a singleton instance is created at the beginning of the application, it is called pre loading singleton mode; If the singleton constructor is called only when the getInstance method is called for the first time, it is called deferred loading singleton mode.

Singleton mode has only one instance in memory, which reduces the memory expenditure. Especially when an object needs to be created and destroyed frequently, and the performance cannot be optimized during creation or destruction, the advantage of singleton mode is very obvious.

When you want to instantiate a singleton class, you must remember to use the corresponding method to get the object instead of new.

Singleton mode is a relatively simple mode among the 23 modes and is widely used,

For example, in Spring, each Bean is singleton by default. The advantage of this is that the Spring container can manage the life cycle of these beans, decide when to create them, when to destroy them, how to deal with them when to destroy them, and so on. If the non singleton mode (Prototype type) is adopted, the management of the initialized Bean is handed over to the J2EE container, and the Spring container no longer tracks the life cycle of the managed Bean.

In JDK, Java Lang. runtime is the classic hungry man singleton mode

Topics: Java Algorithm Singleton pattern