23 design patterns of Java -- II. Singleton pattern

The source code is in mine github and gitee Get in

introduce

Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is a creation pattern, which provides the best way to create objects.
This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique object, which can be accessed directly without instantiating the object of this class.
be careful:

  • 1. A singleton class can only have one instance.
  • 2. A singleton class must create its own unique instance.
  • 3. A singleton class must provide this instance to all other objects.

Implementation mode

Hungry Chinese singleton (static constant, thread safety)

As the name suggests, the hungry Chinese singleton is "hungry", so you create a unique singleton instance at the beginning, but if you haven't used this instance, it will cause a waste of memory

/**
* Hungry Han style single case
 * Advantages: simple, the instantiation is completed when the class is loaded, avoiding the problem of thread synchronization and thread safety
 * Disadvantages: since this class has been instantiated, if this instance is not used from beginning to end, it will cause a waste of memory
  */
public class SingletonTest01 {
    public static void main(String[] args) {
        Signleton instance1= Signleton.getInstance();
        Signleton instance2 = Signleton.getInstance();
        System.out.println(instance1==instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}
class Signleton{
    //1. The constructor is privatized and cannot be created externally through new
    private Signleton(){ }
    //2. Create object instances internally
    private final static Signleton instance = new Signleton();
    //3. Provide a public static method to return the instance object
    public final static Signleton getInstance(){
        return instance;
    }
}

Output results

true
1163157884
1163157884

You can see that the output is the same instance

Hungry Han style singleton (static code block, thread safety)

It is similar to the previous method, except that the class instantiation process is placed in the static code block, that is, when the class is loaded,

Just execute the code in the static code block. The advantages and disadvantages are the same as before

/**
 * It is similar to the previous method, except that the class instantiation process is placed in the static code block, that is, when the class is loaded,
 * Just execute the code in the static code block. The advantages and disadvantages are the same as before
 */
public class SingletonTest02 extends Thread{
    public static void main(String[] args) {
        Signleton instance1= Signleton.getInstance();
        Signleton instance2 = Signleton.getInstance();
        System.out.println(instance1==instance2);
        System.out.println(instance1.hashCode());
        System.out.println(instance2.hashCode());
    }
}
class Signleton{
    //1. The constructor is privatized and cannot be created externally through new
    private Signleton(){}
    //2. Create object instances internally
    private static Signleton instance;

    static {//Static code blocks are used to create singleton objects
        instance = new Signleton();
    }
    //3. Provide a public static method to return the instance object
    public final static Signleton getInstance(){
        return instance;
    }
}

output

true
1163157884
1163157884

Lazy (thread unsafe)

Similarly, as the name suggests, the lazy form is very lazy. It creates an instance only when you use it.

/**
 * Starving - thread unsafe
 * Advantages: it has the effect of lazy loading, but it can only be used under single thread
 * If in multithreading, if a thread enters the if judgment statement block,
 * If another thread enters the judgment statement before it has time to execute downward, multiple instances will be generated (contrary to the singleton mode),
 * Don't use this method in actual development
 */
public class SingletonTest03 {
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
        }
    }
}
class Signleton{
    private static Signleton instance;
    private Signleton(){}
    //Provide a static public method. Create instance only when the method is called
    public static Signleton getInstance(){
        if(instance == null){//If it is empty, create the object again
            instance = new Signleton();
        }
        return instance;
    }
}

output

546405844
135417039
135417039
802181073
135417039
135417039
135417039
802181073
135417039
135417039

Here I choose an extreme case. If your computer is well configured, it may run several times and the results are consistent with the singleton mode.

Lazy (synchronous method, thread safety)

The reason why the above method is thread unsafe is that in the case of multiple threads, multiple threads may judge whether to create a single instance at the same time. To solve this problem, you only need to synchronize the getInstance() method

/**
 * It solves the problem of thread insecurity
 * However, the efficiency is greatly reduced. When each thread wants to obtain an instance, it needs to synchronize the execution of getInstance () method
 */
public class SingletonTest04 {
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
        }
    }
}

class Signleton{
    private static Signleton instance;

    private Signleton(){}

    //Provide a static public method. Create instance only when the method is called
    public static synchronized Signleton getInstance(){
        if(instance == null){//If it is empty, create the object again
            instance = new Signleton();
        }
        return instance;
    }
}

result

802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073
802181073

However, synchronized is a very heavy synchronization lock, and we synchronize every time we execute getInstance(), which greatly affects the efficiency

Lazy (double check, thread safe)

Double check lock, also known as double check lock, combines the advantages and disadvantages of lazy type and hungry type. In the above code implementation, the feature is that a layer of if condition judgment is added inside and outside the synchronized keyword, which not only ensures thread safety, but also improves execution efficiency and saves memory space compared with direct locking

/**
 * Lazy mode - double check
 * Two if judgment checks are performed to ensure thread safety
 * Determine whether it needs to be instantiated again by judging whether it is empty
 */
public class SingletonTest05 {
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
        }
    }
}

class Signleton{
    private static volatile Signleton instance;//volatile ensures visibility
    private Signleton(){}
    //It provides a static thread check and a solution to the problem of lazy code
    public static Signleton getInstance() {
        if (instance == null) {
            synchronized (Signleton.class) {
                if (instance == null) {
                    instance = new Signleton();
                }
            }
        }
        return instance;
    }
}

Operation results

79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097

Recommended use

Static inner class (thread safe)

/**
 * Static inner classes implement singleton mode
 * This method adopts the class loading mechanism to ensure that there is only one thread when initializing the instance
 * The static inner class will not be instantiated immediately when the Signleton class is loaded, but only when instantiation is required
 * The static properties of a class are initialized only when the class is first loaded
 * It avoids thread insecurity and uses static internal classes to realize lazy loading with high efficiency
 */
public class SingletonTest07 {
    public static void main(String[] args) {
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> System.out.println(Signleton.getInstance().hashCode()) ).start();
        }
    }
}
class Signleton{
    //Constructor private
    private Signleton(){}
    //Static inner class, which has a static attribute signeton
    private static class SignletonInstance{
        private static final Signleton instance = new Signleton();
    }
    //Provide a static public method and directly return signletoninstance instance
    public static Signleton getInstance() {
        return SignletonInstance.instance;
    }
}

result

79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097
79372097

This method is relatively simple and recommended

Enumeration (thread safe)

/**
 * @author codermy
 * @createTime 2020/5/14
 * Enumeration method to implement singleton mode
 * 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
 */
public class SingletonTest08 {
    public static void main(String[] args) {
        Singleton singleton = Singleton.INSTANCE;
        singleton.Ok();
        for (int i = 0; i <10 ; i++) {
            new Thread(() -> System.out.println(Singleton.INSTANCE.hashCode()) ).start();
        }

    }
}
enum Singleton{
    INSTANCE;//attribute
    public void Ok(){
        System.out.println("ok");
    }
}

result

ok
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792
858497792

It can be seen that enumeration implements the singleton mode, which is the most concise and recommended. But it is precisely because of its simplicity that the readability is poor

Tags: Design Pattern Back-end

Posted by Kunax on Sat, 21 May 2022 05:12:22 +0300