Four creation methods of singleton mode

Posted by zang8027 on Wed, 15 Dec 2021 19:29:33 +0100

Singleton mode: a class ensures that there is only one global instance and provides a global access point

There are many ways to create singletons. Here we mainly introduce four of them: hungry, lazy, static internal classes and enumeration

Hungry Han style

As the name suggests, creating a singleton in hungry Chinese style means that the singleton will be created when the class is loaded whether it is used or not. The JVM ensures thread safety and is the simplest creation method. The only disadvantage is that it is not created on demand. The following is the instantiation of static variables and the use of getInstance() method.

/**
* @author lyy
* @Title: Singleton mode
* @description:Hungry Chinese style, instantiate a singleton after the class is loaded into memory. The JVM ensures thread safety, is simple and practical, but the only disadvantage is that it will be instantiated when the class is loaded whether it is used or not
* @date 2021/12/13 10:47
*/
public class Singleton01 {
    private static final Singleton01 INSTANCE = new Singleton01();

    //If the constructor is set to private, the static INSTANCE instance must be obtained from gerInstance() when creating an INSTANCE
    private Singleton01(){};

    public static Singleton01 getInstance(){return INSTANCE;};

    public static void main(String[] args) {
        Singleton01 instance01 = Singleton01.getInstance();
        Singleton01 instance02 = Singleton01.getInstance();
        System.out.println(instance01 == instance02);//true indicates the same instance
    }
}

Lazy style

Lazy singleton creation can achieve the purpose of creating on-demand, but thread safety cannot be guaranteed. Thread safety needs to be guaranteed and locks need to be added.

Thread unsafe condition:

/**
 * @author lyy
 * @Title:lazy loading Lazy style
 * @description:It meets the requirements of on-demand instantiation, but it will lead to thread insecurity
 * @date 2021/12/13 10:47
 */
public class Singleton02 {
    //final cannot be added. final must be initialized;
    private static Singleton02 INSTANCE;

    private Singleton02 (){}

    //First judge whether the INSTANCE exists. If it already exists, you don't need to create it
    public static Singleton02 getInstance(){
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            INSTANCE = new Singleton02();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        //Lazy (multithreading problem),
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton02.getInstance().hashCode());
            }).start();
        }
    }

}

The output result shows that the hash address is not unique, indicating that the thread is unsafe

Thread safe situations:

/**
 * @author lyy
 * @Title:Lazy and synchronized can solve the thread safety problem, but it will also reduce efficiency
 * @description:
 * @date 2021/12/13 10:47
 */
public class Singleton03 {
    //final cannot be added. final must be initialized;
    private static Singleton03 INSTANCE;

    private Singleton03 (){}

    //First judge whether the INSTANCE exists. If it already exists, you don't need to create it
    public static synchronized Singleton03 getInstance(){
        if (INSTANCE == null) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e){
                e.printStackTrace();
            }
            INSTANCE = new Singleton03();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        //Lazy (multithreading problem),
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton03.getInstance().hashCode());
            }).start();
        }
    }
}

From the above code, it can be found that competing locks are required every time, which will lead to low efficiency. Therefore, in order to improve efficiency, we create a single example by synchronizing code blocks, as follows:

/**
 * @author lyy
 * @Title:The lazy way improves the low efficiency after locking by synchronizing code blocks, but it can not solve the problem of thread safety
 * @description:
 * @date 2021/12/13 10:47
 */
public class Singleton04 {
    //final cannot be added. final must be initialized;
    private static Singleton04 INSTANCE;

    private Singleton04 (){}

    //First judge whether the INSTANCE exists. If it already exists, you don't need to create it
    public static  Singleton04 getInstance(){
        if (INSTANCE == null) {
            //Synchronous code block
            synchronized(Singleton04.class){
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e){
                    e.printStackTrace();
                }
                INSTANCE = new Singleton04();
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        //Lazy (multithreading problem),
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton04.getInstance().hashCode());
            }).start();
        }
    }
}

This efficiency problem is improved, but it causes a fatal thread insecurity problem, because when two threads simultaneously judge that instance is null, they will compete for locks together. After the previous thread creates a single instance, the next thread will create a single instance after obtaining the lock. The results are as follows

Therefore, there is a double-layer inspection to solve the problems of low efficiency and thread safety

/**
 * @author lyy
 * @Title:Lazy double-layer inspection solves the problems of low efficiency and thread safety
 * @description:
 * @date 2021/12/13 10:47
 */
public class Singleton05 {
    //final cannot be added. final must be initialized;
    private static volatile Singleton05 INSTANCE;

    private Singleton05 (){}

    //First judge whether the INSTANCE exists. If it already exists, you don't need to create it
    public static  Singleton05 getInstance(){
        //First layer inspection
        if (INSTANCE == null) {
            //Synchronous code block
            synchronized(Singleton05.class){
                // Second layer inspection
                if (INSTANCE == null) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e){
                        e.printStackTrace();
                    }
                    INSTANCE = new Singleton05();
                }
            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        //Lazy (multithreading problem),
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton05.getInstance().hashCode());
            }).start();
        }
    }
}

Static inner class

Because the internal class will not be loaded when the external class is loaded, creating a singleton by using the static internal class can ensure thread safety and lazy loading

/**
 * @author lyy
 * @Title:The singleton is created by static internal classes, and the JVM guarantees the singleton
 * @description:
 * @date 2021/12/13 10:47
 */
public class Singleton06 {
    //Private constructor
    private Singleton06 (){}
    //The static internal class is instantiated only once, and the internal class will not be loaded every time the external class is called
    private static class Singleton6Holder{
        private static final  Singleton06 INSTANCE = new Singleton06();
    }
    public static Singleton06 getInstance(){
        return Singleton6Holder.INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton06.getInstance().hashCode());
            }).start();
        }
    }
}

Enum enum

Using enumeration to create a singleton can not only ensure thread safety, but also prevent serialization, because enumeration does not have a constructor

/**
 * @author lyy
 * @Title:Enum enum can not only solve thread synchronization, but also prevent deserialization
 * @description:
 * @date 2021/12/13 10:47
 */
public enum Singleton07 {
    INSTANCE;

    public static void main(String[] args) {
        for (int i = 0; i < 100 ; i++) {
            new Thread(() ->{
                /**
                 * It is found that there will be different values in 100 hash addresses, so it indicates that they are not the same instance
                 */
                System.out.println(Singleton07.INSTANCE.hashCode());
            }).start();
        }
    }
}

Topics: Java Singleton pattern