The singleton pattern is a creation design pattern in which a class has only one instance in the virtual machine. The core idea of implementing singleton pattern is to privatize constructors, which are mainly implemented in two ways: lazy and hungry.

The singleton pattern is a creation design pattern in which a class has only one instance in the virtual machine. The core idea of implementing singleton pattern is to privatize constructors, which are mainly implemented in two ways: lazy and hungry.

Hungry – Thread safe

Public class HungryTypeSingleByFinal {private static final HungryTypeSingleByFinal hungryTypeSingle = new HungryTypeSingleByFinal(); public static HungryTypeSingleByFinal getInstance(){ return hungryTypeSingle; } private HungryTypeSingleByFinal(){} }Copy the code

Hungry – Thread safe

As above, initialization is done when the class is loaded

/ / the hungry type singleton public class HungryTypeSingleByStaticBlock {private static HungryTypeSingleByStaticBlock hungryTypeSingleByStaticBlock; static { hungryTypeSingleByStaticBlock = new HungryTypeSingleByStaticBlock(); } private HungryTypeSingleByStaticBlock(){} public static HungryTypeSingleByStaticBlock getInstance(){ return hungryTypeSingleByStaticBlock; }}Copy the code

Lazy – Threads are not safe

LazyTypeSingleNoSafe {private static LazyTypeSingleNoSafe LazyTypeSingleNoSafe; public static LazyTypeSingleNoSafe getInstance(){ if(lazyTypeSingleNoSafe == null){ lazyTypeSingleNoSafe = new LazyTypeSingleNoSafe(); } return lazyTypeSingleNoSafe; } private LazyTypeSingleNoSafe(){} }Copy the code

Lazy – method locking – thread safety

Public class LazyTypeSingleSafe {private static LazyTypeSingleSafe LazyTypeSingleSafe; public static synchronized LazyTypeSingleSafe getInstance(){ if(lazyTypeSingleSafe == null){ lazyTypeSingleSafe = new LazyTypeSingleSafe(); } return lazyTypeSingleSafe; } private LazyTypeSingleSafe(){} }Copy the code

Slacker – double checked – Thread safety

/ / LanHanShi singleton + double lock check public class LazyTypeSingleSafeDoubleCheck {private static LazyTypeSingleSafeDoubleCheck lazyTypeSingleSafeDoubleCheck; public static LazyTypeSingleSafeDoubleCheck getInstance(){ if(lazyTypeSingleSafeDoubleCheck == null){ synchronized (LazyTypeSingleSafeDoubleCheck.class){ if(lazyTypeSingleSafeDoubleCheck == null){ lazyTypeSingleSafeDoubleCheck = new LazyTypeSingleSafeDoubleCheck(); } } } return lazyTypeSingleSafeDoubleCheck; } private LazyTypeSingleSafeDoubleCheck(){} }Copy the code

It relative to the advantage of the approach is above, if has been instantiated directly return objects, rather like the above into every time synchronization method, double check just at the time of an uninitialized object locking, once objects have been initialized, the back of the thread directly obtained without locking the singleton, undoubtedly reduced the cost.

On the surface it looks thread-safe and the logic is fine, but there are bugs, which WE’ll talk about later

Lazy – Static inner Class – Thread-safe (recommended)

/ / LanHanShi singleton Static inner class implements (recommended) public class LazyTypeSingleSafeInnerClass {private static class LazyTypeSingleSafeInnerClassHolder {  private static LazyTypeSingleSafeInnerClass singleSafe = new LazyTypeSingleSafeInnerClass(); } public static LazyTypeSingleSafeInnerClass getInstance(){ return LazyTypeSingleSafeInnerClassHolder.singleSafe; } private LazyTypeSingleSafeInnerClass(){} }Copy the code

This way is the recommended way, from the external static inner class cannot access LazyTypeSingleSafeInnerClassHolder, LazyTypeSingleSafeInnerClass. Only when the call getInstance method, To get the singleSafe. To note here is that singleSafe object initialization time is not in the singleton class LazyTypeSingleSafeInnerClass is loaded, but the call to getInstance method, LazyTypeSingleSafeInnerClassHolder makes a static inner class 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.

Unbreakable singleton pattern

All of the above singleton patterns can be used to construct new objects by reflection, after all, reflection is very beautiful:

public static void testLazyTypeSingleSafeInnerClass() throws Exception {
    System.out.println(LazyTypeSingleSafeInnerClass.getInstance() == LazyTypeSingleSafeInnerClass.getInstance());
    //true
    Class<LazyTypeSingleSafeInnerClass> innerClassClass = LazyTypeSingleSafeInnerClass.class;
    Constructor<LazyTypeSingleSafeInnerClass> constructor = innerClassClass.getDeclaredConstructor(null);
    constructor.setAccessible(true);

    System.out.println(constructor.newInstance(null) == constructor.newInstance(null));
    //false
}

public static void threadEnvTest() throws InterruptedException {
    Set<LazyTypeSingleNoSafe> typeSingleNoSafeList = new HashSet<>();

    AtomicInteger atomicInteger = new AtomicInteger(0);

    while(atomicInteger.getAndSet(atomicInteger.intValue() + 1) < 50){
        new Thread(()->{
            LazyTypeSingleNoSafe instance = LazyTypeSingleNoSafe.getInstance();
            typeSingleNoSafeList.add(instance);
        }).start();
    }

    for(LazyTypeSingleNoSafe lazyTypeSingleNoSafe: typeSingleNoSafeList){
        System.out.println(lazyTypeSingleNoSafe);
    }
    //single.LazyTypeSingleNoSafe@6e5802d8
    //single.LazyTypeSingleNoSafe@6706a70b
    //single.LazyTypeSingleNoSafe@51a22f1d
}
Copy the code

As we can see from the above example, everything is brother in reflection, so it is impossible to make true singletons, but we can implement absolute singletons and multiple singletons by enumerating this property!

AbsoluteSingleSafe {ONLY_ONE_SINGLE} public enum AbsoluteSingleSafe {ONLY_ONE_SINGLE}Copy the code

Let’s try cracking enumerations:

public static void testAbsoluteSingleSafe() throws Exception {
    System.out.println(AbsoluteSingleSafe.ONLY_ONE_SINGLE == AbsoluteSingleSafe.ONLY_ONE_SINGLE);
    //true

    Class<AbsoluteSingleSafe> absoluteSingleSafeClass = AbsoluteSingleSafe.class;
    Constructor<AbsoluteSingleSafe> constructor = absoluteSingleSafeClass.getDeclaredConstructor(null);
    constructor.setAccessible(true);
    System.out.println(constructor.newInstance(null) == constructor.newInstance(null));
    //Exception in thread "main" java.lang.NoSuchMethodException: single.AbsoluteSingleSafe.<init>()
}
Copy the code

DoubleCheck hidden trouble

Let’s review the DoubleCheck code:

public class LazyTypeSingleSafeDoubleCheck { private static LazyTypeSingleSafeDoubleCheck lazyTypeSingleSafeDoubleCheck;  public static LazyTypeSingleSafeDoubleCheck getInstance(){ if(lazyTypeSingleSafeDoubleCheck == null){ synchronized (LazyTypeSingleSafeDoubleCheck.class){ if(lazyTypeSingleSafeDoubleCheck == null){ lazyTypeSingleSafeDoubleCheck = new LazyTypeSingleSafeDoubleCheck(); } } } return lazyTypeSingleSafeDoubleCheck; } private LazyTypeSingleSafeDoubleCheck(){} }Copy the code

What’s the problem?

We can assume that two threads are accessing the getInstance method one after the other, while thread A is building an object and thread B is just entering the method:

The surface appears to be A problem, or the Instance has not been thread A build, thread B to perform the if (lazyTypeSingleSafeDoubleCheck = = null) get true; Either the Instance has been thread A building completed, thread B execution if gets A false (lazyTypeSingleSafeDoubleCheck = = null). Is that so? The answer is no. This involves instruction reordering by the JVM compiler.

A simple lazyTypeSingleSafeDoubleCheck = new lazyTypeSingleSafeDoubleCheck (); The JVM instructions are compiled by the compiler as follows:

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 and 3, the Instance object is not initialized yet, but it no longer points 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:

Thread A has not finished initialization, but thread B detects that the object is not empty and returns an empty object!! So how can it be avoided? Simply add the modifier volatile to the instance object, as described in the relearning volatile article. So the complete double-checked code is:

/ / LanHanShi singleton + double lock check public class LazyTypeSingleSafeDoubleCheck {private volatile static LazyTypeSingleSafeDoubleCheck lazyTypeSingleSafeDoubleCheck; public static LazyTypeSingleSafeDoubleCheck getInstance(){ if(lazyTypeSingleSafeDoubleCheck == null){ synchronized (LazyTypeSingleSafeDoubleCheck.class){ if(lazyTypeSingleSafeDoubleCheck == null){ lazyTypeSingleSafeDoubleCheck = new LazyTypeSingleSafeDoubleCheck(); } } } return lazyTypeSingleSafeDoubleCheck; } private LazyTypeSingleSafeDoubleCheck(){} }Copy the code

Summary of the singleton pattern

So if you want to implement thread-safe singletons, you can use hunchman, DoubleCheck (volatile version), static inner classes, enumerations, etc. To use lazy loading, you can’t use enumerations, you can only use DoubleCheck (volatile version), static inner classes; Enumerations are the only way to implement singletons that cannot be cracked by reflection, but reflection is generally not deliberately excluded. So the recommended solution is static inner class, simple, practical and thread-safe.