[design mode] 23 design modes - single case mode

Posted by R4nk3d on Thu, 16 Dec 2021 10:25:06 +0100

Article benefits: Alibaba coding specification - download of each version

In programming, we often only need a global instance. For example, the task manager, database connection pool, thread pool, etc. in Windows use the singleton mode.

There are many ways to implement Singleton.

To implement a singleton, consider the following:

1. Who creates the object instance: you can't give it to others, so you can only create the object yourself, so the construction method needs to be set to private

2. Can only one instance be guaranteed in a multithreaded environment

Let's learn several common implementation methods

Hungry Chinese implementation:

public class SingletonTest { 
    // An instance is created whenever the class is loaded into the JVM 
    private static SingletonTest instance = new SingletonTest(); 

    // Private constructs ensure that instances cannot be created by others 
    private SingletonTest(){} 

    // Provide an interface to the outside world to obtain a unique instance 
    public static SingletonTest getInstance(){ 
        return instance; 
    } 
}

Test: get two instances respectively and compare whether their memory addresses are equal

public static void main(String[] args) { 
    SingletonTest instance1 = SingletonTest.getInstance(); 
    SingletonTest instance2 = SingletonTest.getInstance(); 
    System.out.println(instance1 == instance2); // true 
}

The result is true and the thread is safe

Lazy implementation

public class SingletonTest02 { 

    // Declare an instance 
    private static SingletonTest02 instance; 

    // Private constructs ensure that instances cannot be created by others 
    private SingletonTest02(){} 

    // Provide an interface to the outside world to obtain a unique instance 
    public static SingletonTest02 getInstance(){ 
        if (instance == null){ 

            try { 
                Thread.sleep(1); 
            } catch (InterruptedException e) { 
            } 

            // Create an instance when called 
            instance = new SingletonTest02(); 
        } 
        return instance; 
    } 

    public static void main(String[] args) { 
        for (int i=0; i<100; i++){ 
            new Thread(() -> { 
                System.out.println(SingletonTest02.getInstance().hashCode());
            }).start(); 
        } 
    } 
}

Test: start 100 threads to get the instance at the same time and print its hashcode. give the result as follows

1406241281 
1406241281
1406241281
1406241281
898323977
139505508
139505508
103512977

hashcode is different, which means that this writing method produces multiple instances in a multithreaded environment, which is contrary to the topic

Improved lazy style

Improvement scheme: synchronous lock + double judgment

// Provide an interface to the outside world to obtain a unique instance 
public static SingletonTest03 getInstance(){ 

    // First judgment 
    if (instance == null){ 

        try { 
            Thread.sleep(1); 
        } catch (InterruptedException e) { 
        } 

        // Create an instance when called 
        synchronized(SingletonTest03.class){ 

            // Second judgment 
            if (instance == null){ 
                instance = new SingletonTest03(); 
            } 
            return instance; 
        } 

    } 
    return instance; 
}

explain:

The first one is empty. Some people think it can be removed. After removal, there is no thread safety problem in terms of thread safety, but it is not the best in terms of execution efficiency. If it is removed, how many threads must hold how many locks, which is inefficient.

Another lazy implementation - static inner class implementation

public class SingletonTest04 { 

    // Private constructs ensure that instances cannot be created by others 
    private SingletonTest04(){} 

    // Private inner class 
    private static class Inner{ 

        // Declare an instance in the inner class 
        private static final SingletonTest04 instance = new SingletonTest04();

    }

    // Provide an interface to the outside world to obtain a unique instance 
    public static SingletonTest04 getInstance(){ 
        return Inner.instance; 
    } 

    public static void main(String[] args) { 
        for (int i=0; i<100; i++){ 
            new Thread(() -> { 
                System.out.println(SingletonTest04.getInstance().hashCode());
            }).start(); 
        } 
    } 
}

Note: the above writing method is still lazy loading. Who ensures that there is only one instance under multithreading? It is the JVM itself. The JVM loads the internal class only when it obtains the instance, and it only loads it once. Ensuring an instance is the same as hungry Chinese style.

With so many single mode implementations, one of the authors of developing the Java language can't see it anymore. He can solve the problem directly

Enumeration implementation

public enum SingletonTest05 { 

    instance; 

    public static void main(String[] args) { 
        for (int i=0; i<100; i++){ 
            new Thread(() -> { 
                System.out.println(SingletonTest05.instance.hashCode());
            }).start(); 
        } 
    } 
}

Advantages of using enumeration to implement singleton:

1. Ensure thread safety

2. Prevent deserialization from creating objects

The above are the common implementation methods of single instance, each with its advantages and disadvantages. However, in the actual design of single instance, the problems considered are not only thread safety, such as whether the common distributed cluster (multi JVM process) is an object instance, whether the instance is a single instance, and whether the instance is maliciously created after reverse sequencing.

--- END ---

Pay attention to the official account of Java Island, making millions of points every day, and grow up together with Xiaobian Java!

Topics: Design Pattern Singleton pattern