————— the next day —————















Singleton pattern version 1:



public class Singleton {
    private Singleton() {} private static Singleton instance = null; Static factory method public static SingletongetInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Why do I write that? Let’s explain a few key points:


Signleton’s constructor is private because a class can only build one object, so it can’t just do new operations.


2. Instance is a static member of the Singleton class and is also our Singleton object. Its initial value can be written as Null or as new Singleton(). The difference will be explained later.


3. GetInstance is a method to get a singleton object.


If the singleton initial value is NULL and has not yet been built, the singleton object is built and returned. This is the slacker pattern of the singleton pattern.



If the Singleton is first actively built by new Singleton(), null nulls are no longer required, which is hunger-hander writing.


The names are very graphic: the hungry man looking for food, the lazy man lying on the ground waiting to be fed.







Why is this code not thread safe?


Assuming the Singleton class has just been initialized and the instance object is still empty, both threads call the getInstance method at the same time:




Because Instance is empty, both threads pass the condition at the same time and start new:





Thus, it is clear that Instance has been constructed twice. Let’s make a change to the code:



Singleton Pattern 2nd edition:



public class Singleton {
    private Singleton() {} private static Singleton instance = null; Static factory method public static SingletongetInstance() {
        if(instance == null) {synchronized (singleton.class){// synchronized (singleton.class)if(instance == null) {// Double check mechanism instance = new Singleton(); }}}returninstance; }}Copy the code


Why do I write that? Let’s explain a few key points:


1. To prevent the new Singleton from being executed more than once, a Synchronized lock is preceded by the new operation to lock the entire class (note that object locks cannot be used here).


2. After entering the Synchronized critical zone, one more short call is required. Because when two threads access simultaneously, thread A has built the object and thread B has passed the initial nullation validation, thread B will still build the instance object again without the second nullation.













A two-call mechanism like this is called double detection.













— — — — — —











Imagine A scenario where two threads, one after the other, access the getInstance method while thread A is building an object and thread B has just entered the method:




Either Instance has not been built by thread A, and thread B gets true if (Instance == null); Either Instance has already been built by thread A, and thread B gets false if (Instance == null).


Is that true? The answer is no. This involves instruction reordering by the JVM compiler.


What does reordering mean? For example, a simple sentence in Java instance = new Singleton is compiled by the compiler into the following JVM instruction:


memory =allocate(); //1: allocates memory space for the object

ctorInstance(memory); //2: initializes the object

instance =memory; //3: Sets instance to the newly allocated memory address


However, the order of these instructions is not fixed, and may be optimized by the JVM and CPU to rearrange the instructions in the following order:


memory =allocate(); //1: allocates memory space for the object

instance =memory; //3: Sets instance to the newly allocated memory address

ctorInstance(memory); //2: initializes the object


When thread A finishes executing 1,3, the instance object has not been initialized yet, but is no longer pointing to null. If (instance == null) thread B preempts the CPU resource, the result of executing if (instance == null) is false, which returns an instance object that has not been initialized. As shown below:







How can this be avoided? We need to add the modifier volatile to the instance object.





Singleton Pattern Version 3:


public class Singleton {
    private Singleton() {}  //私有构造函数
    private volatile static Singleton instance = null;  //单例对象
    //静态工厂方法
    public static Singleton getInstance() {
          if(instance == null) {synchronized (singleton.class){// synchronized (singleton.class)if(instance == null) {// Double check mechanism instance = new Singleton(); }}}returninstance; }}Copy the code







The volatile keyword indicates that a value may change between different accesses, it prevents an optimizing compiler from optimizing away subsequent reads or writes and thus incorrectly reusing a stale value or omitting writes.







When instance = new Singleton is executed by thread A, which is volatile, what is the JVM order of execution? Always make sure it is in the following order:


memory =allocate(); //1: allocates memory space for the object

ctorInstance(memory); //2: initializes the object

instance =memory; //3: Sets instance to the newly allocated memory address


Thus, from thread B’s point of view, a reference to an instance object will either point to null or to an instance that has been initialized, without any intermediate state, ensuring safety.









Implement the singleton pattern with static inner classes:


public class Singleton {
    private static class LazyHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    private Singleton (){}
    public static Singleton getInstance() {
        returnLazyHolder.INSTANCE; }}Copy the code


Here are a few things to note:


1. The static inner class LazyHolder is not accessible from the outside. INSTANCE is only available when singleton.getInstance is called.


2. The INSTANCE object is initialized not when the Singleton class is loaded, but when the getInstance method is called so that the static inner class LazyHolder is loaded. Therefore, this approach is to use the loading mechanism of classloader to achieve lazy loading and ensure the thread-safe construction of singleton.











How can reflection be used to break the constraints of singleton patterns? It’s actually pretty simple. Let’s look at the code.



Break singletons with reflection:

/ / get the Constructor Constructor con = Singleton. Class. GetDeclaredConstructor (); // Set con.setaccessible (true); Singleton singleton1 = (Singleton) con.newinstance (); Singleton singleton1 = (Singleton) con.newinstance (); Singleton singleton2 = (Singleton)con.newInstance(); System.out.println(singleton1.equals(singleton2)));Copy the code



The code can be summarized in three simple steps:


The first step is to get the constructor for the singleton class.


Second, make the constructor accessible.


The third step is to construct objects using the newInstance method.


Finally, to verify that the two objects are really different, we compare them using the equals method. No doubt, the comparison result is false.







Implement the singleton pattern with enumerations:


public enum SingletonEnum {
    INSTANCE;
}Copy the code
Copy the code






Let’s do an experiment and still execute the reflection code:


/ / get the Constructor Constructor con = SingletonEnum. Class. GetDeclaredConstructor (); // Set con.setaccessible (true); SingletonEnum Singleton1 = (SingletonEnum) con.newinstance (); SingletonEnum singleton1 = (SingletonEnum) con.newinstance (); SingletonEnum singleton2 = (SingletonEnum)con.newInstance(); System.out.println(singleton1.equals(singleton2)));Copy the code



When executing the get constructor step, the following exception is thrown:


Exception in thread “main” java.lang.NoSuchMethodException: com.xiaohui.singleton.test.SingletonEnum.<init>()

at java.lang.Class.getConstructor0(Class.java:2892)

at java.lang.Class.getDeclaredConstructor(Class.java:2058)

at com.xiaohui.singleton.test.SingletonTest.main(SingletonTest.java:22)

at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)

at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)

at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)

at java.lang.reflect.Method.invoke(Method.java:606)

at com.intellij.rt.execution.application.AppMain.main(AppMain.java:134)













A few points to add:


1. The volatile keyword not only prevents instruction reordering, but also ensures that the value of the variable accessed by the thread is the most recent value in main memory. The details of volatile will be covered in future comics.


2. The singleton pattern implemented with enumeration not only prevents the forced construction of singleton objects by reflection, but also ensures that the return result of the antisequence is the same object when the enumeration class objects are deserialized.


For other implementations of the singleton pattern, if you want both to be serializable and to deserialize to the same object, you must implement the readResolve method.


3. This cartoon is purely entertainment, please cherish the present work as much as possible, do not imitate xiao Hui’s behavior.




— — the END — — –




If you like this article, please long click the picture below to follow the subscription number programmer Xiao Grey and watch more exciting content