Singleton mode (multiple writing methods)

What is singleton mode?

Singleton mode is to ensure that a class has only one instance of an object. The way to realize this function is called singleton mode.

How to implement singleton mode

Because it is guaranteed that a class can only have one instance, cannot be instantiated multiple times, and cannot allow users to new objects, it is necessary to privatize the construction method and let the external obtain object instances by providing class methods
There are two main ways to implement singleton mode: hungry man mode and lazy man mode

Hungry Han style

Hungry Chinese style means that whether you need this object or not, create it first and use it directly when you use it.
Hungry Chinese code implementation:

class Hungry {
    private  static  Hungry instance = new Hungry();
    private Hungry(){
        
    }
    public static Hungry getInstance(){
        return instance;
    }

    public static void gethh(){
        System.out.println("hhh");
    }

}

Hungry Chinese style is thread safe.

Lazy style

Lazy type is to create an object when the instance needs to be used.

class Lazy {
    private  static  Lazy instance;
    private Lazy(){

    }
    public static Lazy getInstance(){
        if(instance == null){
            instance = new Lazy();
        }
        return instance;
    }

    public static void gethh(){
        System.out.println("hhh");
    }
}

There is no problem with the lazy code executing in a single thread, but it may be instantiated many times due to concurrency problems when executing in multiple threads. Therefore, you can lock the lazy type to control that the class is only allowed to be instantiated once.

Lazy (locked version)

class Lazy {
    private  static  Lazy instance;
    private Lazy(){

    }
    public  static Lazy getInstance(){
        synchronized (Lazy.class){
            if(instance == null){
                instance = new Lazy();
            }
        }

        return instance;
    }

    public static void gethh(){
        System.out.println("hhh");
    }
}

The security of singleton mode is guaranteed by locking. However, the granularity of locks is too large, which will seriously affect the performance. Therefore, DCL double locking mechanism can be adopted.

Lazy style (DCL version)

class Lazy {
    private  static  Lazy instance;
    private Lazy(){

    }
    public  static Lazy getInstance(){
        
        if(instance == null){
                synchronized (Lazy.class){
                    if(instance == null){
                        instance = new Lazy();
                    }                     
            }
        }

        return instance;
    }

    public static void gethh(){
        System.out.println("hhh");
    }
}

In this way, the lock is only requested when the object is not created, and the lock will not be obtained after the object is created. This can improve the efficiency of the program and ensure that there is only one instance. However, there are still problems with this code, because the JVM has instruction rearrangement, so it is not necessarily thread safe under multithreading. If instance is empty, the reason is that the instance cannot be read for the first time. Because instance = new Lazy(); It can be completed in the following three steps:

  • memory = allocate(); // 1. Allocate object memory space
  • instance(memory); // 2. Initialize object
  • instance = memory; // 3. Set instance to point to the memory address just allocated. At this time, instance= null
    Because there is no dependency between 2 and 3, instruction rearrangement may occur. There is no problem in single thread, but rearrangement needs to be prohibited in multi thread.
  • memory = allocate(); // 1. Allocate object memory space
  • instance = memory; // 3. Set instance to point to the memory address just allocated. At this time, instance= null
  • instance(memory); // 2. Initialize object

When we execute step 2 after the rearrangement and try to obtain the instance, we will get null, because the initialization of the object has not been completed, but only in step 3 after the rearrangement. Therefore, when we execute the code in the singleton mode, we will re create an instance. Therefore, when a thread accesses an instance that is not null, the instance may not have been initialized, which leads to the problem of thread safety. Instruction rearrangement will only ensure the execution consistency of serial semantics (single thread), but it will not affect the semantic consistency among multiple threads
To solve the above problems, you can use the volatile keyword to prohibit instruction rearrangement and ensure the thread safety of singleton mode.
The final code is:

Lazy style (final version)

class Lazy {
    private volatile   static  Lazy instance;
    private Lazy(){

    }
    public  static Lazy getInstance(){

        if(instance == null){
                synchronized (Lazy.class){
                    if(instance == null){
                        instance = new Lazy();
                    }
            }
        }

        return instance;
    }

    public static void gethh(){
        System.out.println("hhh");
    }
}

Tags: Interview

Posted by Snake PHP on Sat, 16 Apr 2022 22:43:55 +0930