Design Patterns: Five Realizations and Analysis of Singletons

Singleton pattern is the simplest creation pattern in design pattern. The usage scenarios generally include: tool class object, class with only one instance object in the system, creating frequently or time-consuming and frequently used objects, etc. For example, JDK's Runtime class is the starvation singleton mode, and Spring container management instance Bean is also the starvation singleton by default, initialized when the container starts, of course, can also be set to default-lazy-init="true". In order to avoid wasting resources by creating thread pools many times, the common thread pool can also be implemented in the singleton mode.

Hunger patterns

Class loading is initialized by default, which is the simplest but also has some limitations. 1. Some resources may not be ready for class loading. 2. Multiple custom class loaders may cause repeated initialization when loading at the same time.

public class HungrySingleton {

    private static final HungrySingleton instance = new HungrySingleton ();

    private HungrySingleton () {
        // Be sure to remember the privatization constructor
    }

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

Lazy Man Mode (Thread Safety Issues Should Be Payed Attention)

public class LazySingleton {

    private static LazySingleton instance = null;

    private LazySingleton () { }

    /**
     * Be sure to remember to add synchronized
     */
    public static synchronized LazySingleton getInstance() {
        if(null == instance) {
            instance = new LazySingleton ();
        }
        return instance;
    }
}

Double Detection Lock Mode

Note the thread safety hazards: Uninitialized complete instance objects are captured in a multithreaded environment. Modifying objects with volatile can prevent this problem. For a detailed analysis, see the notes below.

public class DoubleCheckSingleton {

    /**
     * TODO This paper mainly uses the "sequentiality" of volatile to ensure that no instruction rearrangement occurs in instance writing and other threads get uninitialized complete objects.
     * TODO Even without volatile, "visibility" can be guaranteed by synchronized
     */
    private static volatile DoubleCheckSingleton instance = null;

    private DoubleCheckSingleton() { }

    public static DoubleCheckSingleton getInstance() {
        if (null == instance) {
            synchronized (DoubleCheckSingleton.class) {
                if (null == instance) {
                    // new DoubleCheckSingleton() is not an atomic operation. The general steps are as follows
                    // Memor = allocate (); // 1: Allocate memory space for objects
                    // CtorInstance (memory); //2: Initialization object
                    // Instance = memory; //3: Set instance to point to the memory address just allocated
                    // Between 2 and 3 in pseudocode, it may be reordered, so if the execution order is 1, 3, 2, the thread will run out of switching threads after 3, and then uninitialized objects may appear.
                    // If volatile is used to modify it, it will create a write-read barrier storeload, forbidding 2,3 instruction replays, thus preventing the problem of uninitialized completeness.
                    // TODO can't enter synchronized code blocks even when the time slice is exhausted and other threads are switched. Because the original code blocks have not been executed yet, the lock will not be released, and there will be no multiple instantiations.
                    instance = new DoubleCheckSingleton();
                }
            }
        }
        return instance;
    }

Static Internal Class Mode (Recommended: Thread Safety Guaranteed by JVM and Simple Code Not Easy to Error)

public class InnerClassSingleton {

    private InnerClassSingleton() { }

    private static class NestClass {
        private static final InnerClassSingleton instance = new InnerClassSingleton();
    }

    public static InnerClassSingleton getInstance() {
        // After compiling javac, NestClass is a separate. class file.
        // NestClass.class file is not automatically loaded when InnerClassSingleton.class is loaded
        // When NestClass.instance is called, the JVM is triggered to load the NestClass class.
        return NestClass.instance;
    }
}

Typesafe Enum pattern

As with static internal classes, the JVM ensures that enum is not reflected and that the constructor method executes only once, so the singleton is thread-safe.

public class EnumSingleton {

    private EnumSingleton() { }

    private enum InnerEnum {
        /**
         * Occupancy Enumeration Value
         */
        enumFactory;
        private EnumSingleton instance;

        /**
         * TODO Note that this is the constructor of the enumeration class
         */
        private InnerEnum() {
            instance = new EnumSingleton();
        }
        public EnumSingleton getInstance() {
            return instance;
        }
    }

    public static EnumSingleton getInstance() {
        return InnerEnum.enumFactory.getInstance();
    }
}

Tags: Java jvm JDK Spring

Posted on Tue, 13 Aug 2019 03:46:38 -0700 by changeback