preface

Welcome the most beautiful and handsome you praise oh ~~~!

The singleton pattern is one of the 23 design patterns in object-oriented programming languages and belongs to the creation design pattern. This is mainly used to solve the problem of frequent creation and destruction of objects, because the singleton pattern guarantees that there will be only one instance of a class. Most of them should know something about singleton mode, but they may not give a complete answer in the interview, so they can’t give themselves extra points or even take points off.

A single knowledge point can not bring extra points in the interview, and the knowledge tree of the system will let the interviewer look at it differently, and this paper will systematically introduce the basic version and the perfect version of the singleton mode, basically will completely include the content of the singleton mode. If you think there are different opinions can leave a message exchange.

Source code has been included github view source code

The most important thing of the singleton pattern is to ensure that only one instance of a class will occur, so more than one instance cannot be called a singleton. All its code consists of the following characteristics.

  1. Privatize the constructor, disallowing the creation of singleton objects from outside.
  2. Provide a global access point to get a singleton.

What is a global access point? Okay, that’s a little too neat, but what if I said public static methods?

A hungry, lazy man

It is mainly divided into hungry and lazy mode. So what’s hungry? What is a slob?

Xiao Li’s father lived a hard life since childhood and experienced the famine years, so he was very nervous about food. When Xiaoli goes to school, no matter whether xiaoli needs it or not, xiaoli will prepare a lot of snacks for her.

On the other hand, Xiao Ming’s father is a very lazy man. He does everything at the end of the day, and he will finish everything only when someone calls him, which leads to the definition of hungry and lazy:

Hungermode: An object is created regardless of whether a singleton is used or not. Hungry and hungry is a waste of resources because it is possible that objects created will never be used.

The code is as follows:

package demo.single;
/**
 * 饿汉模式
 */
public class HungrySingle {
    /** * hungrySingle is created first, regardless of whether the hungrySingle object is used. Since hungrySingle is created before the object is used, there is no thread safety issue
    private static HungrySingle hungrySingle = new HungrySingle();
    /** * privatize the constructor, disallow external creation */
    private HungrySingle(a){}/** * provides the method to get the instance */
    public static HungrySingle getInstance(a){
        returnhungrySingle; }}Copy the code

Lazy mode: Objects are not created first, but are not created until someone uses them. Compared with the hungry-hander mode, the lazy mode does not waste resources, so the lazy mode is basically adopted.

The code is as follows:

package demo.single;
/** * slacker mode */
public class LazySingle {
    /** * lazy mode, the object is not created first, but only created when called */
    private static LazySingle lazySingle = null;
    private LazySingle(a) {}/** * creates an object when called and returns */
    public static LazySingle getInstance(a){
        if(lazySingle == null){
            lazySingle = new LazySingle();
        }
        returnlazySingle; }}Copy the code

Xiao Li: Interviewer, I think my explanation is ok.

Interviewer: It’s fine in a single-threaded environment, but what about in a multi-threaded environment?

Xiao Li: I know that, lock it up!

Interviewer: Go out and turn left and the elevator will go straight to you!

In fact, the lock did not answer wrong, the key problem is how to lock!

The method content of obtaining instance is directly written into synchronous code block, which solves the problem of multi-thread safety, but the problem of concurrent efficiency is exposed. You see, now that the method is locked, whether or not the singleton object is created, the lock is acquired and the lock is released. Such performance is clearly unacceptable.

Xiao Li: Let me see. Emmmmm… ! With that in mind, we can add a judgment around the synchronization block that returns the object if it has already been created.

Interviewer: This solves part of the concurrency problem, but if you have a lot of thread accesses at the time of creation, is there also a concurrency problem? Optimize and optimize.

Xiao li, this is true, if the object has not yet been created, there are A lot of threads to access, also can appear problem, if two threads access at the same time, when A thread priority for the lock, A into the synchronized code block, the B did not fight for locks, will be in A wait state, when A thread execution after the completion of the lock is released, B enters the synchronized code block, at which point thread B also creates an object, breaking the singleton.

Xiao Li: Interviewer, I see. You can add another layer of if judgment in the synchronous code block. If the object has been created, it will return directly.

Double Check

The result is what we call a Double Check. Double lock checking is used in many places, with the following code.

package demo.single; /** * public class LazySingle {/** * public class LazySingle {/** * Private static LazySingle LazySingle = null; Private LazySingle() {} public static LazySingle getInstance(){// First check if(LazySingle == null){ synchronized (LazySingle.class){ //double check if(lazySingle == null){ lazySingle = new LazySingle(); } } } return lazySingle; }}Copy the code

Interviewer: Xiao Li, why don’t you run the code through multiple threads?

Xiao Li: Hurrah! It seems normal. Wait, it doesn’t seem right, there are still multiple objects!! Ahhh, why is that? I’m confused. It’s totally out of my depth.

Interviewer: Ha ha, boy. Now you know who’s the boss? I to give you a good explanation, in fact, this has nothing to do with our code, normal, should not be such a problem, but we all know, the code in the process of operation, will be compiled into a a command to run, but the JVM at runtime, the guarantee under the condition of single thread the end result is not affected, optimize the instruction, It is possible to reorder instructions, which also breaks singletons.

lazySingle = new LazySingle();
// This code generates three instructions at run time, namely: 1. Allocate memory space 2. Create an object. 3. Point to a reference
// The JVM optimizer rearranges the instructions in the following order: 1. Allocate memory space. 3. Point to a reference. 2
// In a single thread, this optimization is fine, but in multithreading, threads are competing for CPU time fragments. Suppose A has just finished executing 1 3 // instructions. At this point, B grabs the time fragment, finds that the object is not empty, and returns directly, but the object has not been created. B calls
// This object throws an exception
// Volatile prevents instruction reordering and ensures that instructions are executed in 1, 2, 3 order.
// Add the volatile modifier
private volatile  static LazySingle lazySingle = null;
Copy the code

Xiao Li: Finally, it’s so difficult. There are so many details in a simple singleton pattern.

Interviewer: You think that’s it?

A singleton of an inner class

The singleton pattern is perfectly implemented using inner classes, and the implementation code is very simple.

package demo.single;

/** * inner class way to implement singleton */
public class InnerSingle {
    /** * privatize constructor */
    private InnerSingle(a){}/** * Private inner class */
    private static class Inner{
        // The Jingtai inner class holds the objects of the outer class
        public static final InnerSingle SINGLE = new InnerSingle();
    }
    /** * returns the object held by the static inner class */
    public static InnerSingle getInstance(a){
        returnInner.SINGLE; }}Copy the code

As you can see, there are no synchronized methods or blocks in the code, so how can a static inner class be a safe singleton?

  1. When an external class is loaded, the inner class is not loaded immediately, but when called.
  2. No matter how many threads are accessed, the JVM must ensure that classes are properly initialized, that is, static inner classes are thread-safe at the JVM level

The downside, of course, is that static inner classes can be cumbersome if you need to pass arguments when creating a singleton.

Destroy the singleton

So, is the singleton above perfect? No, watch how I break the singleton.

Reflective damage

Reflection can bypass the limitations of private constructors and create objects. Of course, normal calls don’t break singletons, but what if someone does, as in the following call?

package demo.single;

import java.lang.reflect.Constructor;
/** * reflection destruction singleton */
public class RefBreakSingleTest {
    public static void main(String[] args) throws Exception {
        // Get the class object
        Class<LazySingle> lazySingleClass = LazySingle.class;
        // Get the constructor
        Constructor<LazySingle> constructor = lazySingleClass.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        // Create an object
        LazySingle lazySingle = constructor.newInstance(null); System.out.println(lazySingle); System.out.println(LazySingle.getInstance()); System.out.println(lazySingle == LazySingle.getInstance()); }}Copy the code

It is obvious that two different cashes have occurred. Obviously, the singleton is broken! How do you ban this? I looked up a lot of information on the Internet, mostly using variable control method, that is, adding a variable to the class to determine whether the constructor of the singleton class is called, the code is as follows.

Private static Boolean isInstance = false; private volatile static LazySingle lazySingle = null; private LazySingle() throws Exception { if(isInstance){ throw new Exception("the Constructor has be used"); } isInstance = true; }Copy the code

When you call the test code again and find that you can’t create more than one singleton, the program throws an exception.But don’t forget that attributes can also be modified by reflection (count and instance judgment reflections can be bypassed).

public class RefBreakSingleTest {
    public static void main(String[] args) throws Exception {
        // Get the class object
        Class<LazySingle> lazySingleClass = LazySingle.class;

        // Get the constructor
        Constructor<LazySingle> constructor = lazySingleClass.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        // Create an object
        LazySingle lazySingle = constructor.newInstance(null);
        System.out.println(lazySingle);
        Field isInstance = lazySingleClass.getDeclaredField("isInstance");
        isInstance.setAccessible(true);
        isInstance.set(null.false); System.out.println(LazySingle.getInstance()); System.out.println(lazySingle == LazySingle.getInstance()); }}Copy the code

Singleton is broken again, feeling is not already fast collapse, a singleton why so many things!! Since private properties, private methods can be retrieved externally by reflection, is there anything that reflection can’t? I also found an alternative on the web, a private inner class to hold instance control variables, and I’ve tested that reflection can also bypass and break singletons.

package demo.pattren.single;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

public class BreakInnerTest {
    public static void main(String[] args) throws Exception {
        Class<LazySingle> lazySingleClass = LazySingle.class;
// // gets the constructor
        Constructor<LazySingle> constructor = lazySingleClass.getDeclaredConstructor(null);
        constructor.setAccessible(true);
        // Create an object
        LazySingle lazySingle = constructor.newInstance(null);
        // Get the class object of the inner classClass<? > aClass = Class.forName("demo.pattren.single.LazySingle$InnerClass"); Method[] methods = aClass.getMethods(); Constructor<? >[] declaredConstructors = aClass.getDeclaredConstructors(); System.out.println(declaredConstructors); Constructor<? > declaredConstructor = declaredConstructors[0];
        declaredConstructor.setAccessible(true);
        Creating an inner class requires passing in an object of the outer class
        Object o = declaredConstructor.newInstance(lazySingle);
        // Successfully bypassed
        methods[0].invoke(o); }}Copy the code

The web is pretty much full of both, but reflexes are capable of circumventing judgment and doing damage. It can be argued that reflection in this way can be broken, and there is no 100% guarantee that singletons will not be broken. You are welcome to provide perfect examples.

Serialization destruction

Java IO provides object streams for writing and reading objects from disk. This also becomes the singleton’s point of destruction.

    public static void main(String[] args) throws Exception {
        // Get the singleton in the normal way
        InnerSingle instance = InnerSingle.getInstance();

        // Write to disk
        FileOutputStream fos = new FileOutputStream("d:/single");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(instance);
        oos.close();
        fos.close();

        // Reads objects from disk
        FileInputStream fis = new FileInputStream("d:/single");
        ObjectInputStream ois = new ObjectInputStream(fis);
        InnerSingle innerSingle = (InnerSingle) ois.readObject();

        System.out.println(instance);
        System.out.println(innerSingle);
        System.out.println(innerSingle == instance);
    }
Copy the code

Serialization The JVM provides a mechanism to prevent singletons from being corrupted by adding readResovle methods to singletons.

	// When deserializing, the readResolve method directly returns the object specified by the method
    private  Object readResolve(a){
        return getInstance();
    }
Copy the code

Test results:Serialization no longer breaks singletons, but how does the JDK handle all this?

public final Object readObject() throws IOException, ClassNotFoundException { if (enableOverride) { return readObjectOverride(); } int outerHandle = passHandle; Object obj = readObject0(false); Object obj = readObject0(false); handles.markDependency(outerHandle, passHandle); ClassNotFoundException ex = handles.lookupException(passHandle); //more code but not importentCopy the code

Digging deeper, the key code for the readObject0 method is as follows

 		byte tc;
        // Retrieve a byte of the file to determine the type of object to read
        while ((tc = bin.peekByte()) == TC_RESET) {
            bin.readByte();
            handleReset();
        }
        depth++;
        totalObjectRefs++;
        try {
            switch (tc) {
                case TC_NULL:
                    return readNull();
                case TC_ENUM:
                    return checkResolve(readEnum(unshared));
				// It is an object class
                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared));
                //more othrer case
Copy the code

Continue tracing the readOrdinaryObject method to discover the key code for readReslove

		/ / determine whether have readReslove method (desc. HasReadResolveMethod ())
 		if(obj ! =null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod())
        {	
           / / readReslove execution
            Object rep = desc.invokeReadResolve(obj);
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if(rep ! = obj) {// Filter the replacement object
                if(rep ! =null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1); }}// Returns the result of the readReslove methodhandles.setObject(passHandle, obj = rep); }}return obj;
Copy the code

Enumerated singletons – The perfect singleton pattern

In Effective Java, Josh Bloch strongly recommends using enumerations to implement singletons.

package demo.single; public enum EnumSingle { SINGLE; public void doJob(){ System.out.println("doJob"); }}Copy the code

Enumerated types are the best choice for the singleton pattern, thanks largely to the JVM’s support for them:

  1. The JVM guarantees that only one instance of an enumeration type exists
  2. Serialization and deserialization of enumerated types do not break the singleton nature.
  3. Reflection also cannot break enumeration singletons

Enumerations are inherently singletons, so would you choose enumerations as singletons?