Wow, it’s called the simplest design pattern in Java — the singleton design pattern. This can be summed up in ten thousand words! I couldn’t believe it at first, but I believed it!

Summary is not easy, I hope you three even serve! Thank you! Recently, I have also been improving my summarized articles and the learning system of courses in GitHub one by one. I hope you can give me a Star on GitHub! Now my GitHub although not perfect many technical articles, but in a few months after you today, my GitHub will become a complete Java learning system!

GitHub address: github.com/Ziphtracks/…

Everybody remember to give me a Star! Ballpoint pen!

[TOC]


What is the singleton design pattern?

The Singleton Pattern is one of the simplest design patterns in Java. This type of design pattern is the creation pattern, which provides the best way to create objects.

This pattern involves a single class that is responsible for creating its own objects while ensuring that only a single object is created. This class provides a way to access its unique objects directly, without instantiating the objects of the class.

Note:

  • A singleton class can have only one instance.
  • A singleton class must create its own unique instance.
  • The singleton class must provide this instance to all other objects.

The advantages and disadvantages of singleton design pattern

Advantages:

  • There is only one instance object in memory, reducing memory overhead. Fixed the frequent creation and destruction of memory instance objects.
  • Avoid excessive resource usage. For example, write files.

Disadvantages:

  • With no interfaces, no inheritance, and a conflict with the single responsibility principle, a class should only care about internal logic, not how it is instantiated externally.

The use of singleton design pattern

When you want to control the number of instances and save system resources. It also solves the problem of frequent creation and destruction of instance objects in memory within a global context.

Classification of singleton design patterns

The principle of the singleton design pattern is to create unique instances, but there are many ways to create unique instances, resulting in many kinds of singleton design patterns. They are: plain lazy singleton, synchronous locking lazy singleton, synchronous block lazy case, hungry singleton, double check lock (double check lock) singleton, static inner class (register) singleton, and enumeration singleton

Fifth, the transmission process of singleton pattern idea

Question: If we want to have the idea of writing a singleton design pattern, how do we implement the singleton design pattern? How can you create and use only one instantiated object globally? And will there be other problems in the process of use? Write out the basic singleton design pattern with questions first!

First, create a class called Singleton. Then create an object like this:

class Singleton {
    Singleton instance = new Singleton();
}
Copy the code

So if we look at this, we see that this is a normal class, and we create an instance object of a normal class. So if we implement a single instance object, we can’t let the outside world access and create that object. So we came up with constructors, and you know that if you write any constructor in a class, it implicitly has a public constructor with no arguments. At this point, the clever friend thought of private constructor, do not let the outside world create objects. The code is as follows:

class Singleton {
    // Create an instance object of the class inside the class
    private Singleton instance = new Singleton();
    // Privatize the constructor so that it cannot be called outside the class, creating instance objects at will
    private Singleton(a) {}}Copy the code

So what’s next? How do we provide instance objects inside this class to the outside world? Use the static modifier when creating an instance object of a class. I was really smart about it. So if it’s static then it can be used by the class name, okay? So here’s the question. What if the following happens? Look at the following operations!

class Singleton {
    // Create an instance object of the class inside the class. The statically modified object is created only once as the class is loaded
    private static Singleton instance = new Singleton();
    // Privatize the constructor so that it cannot be called outside the class, creating instance objects at will
    private Singleton(a) {}}class Test {
    // Create two objects s1 and s2 using the same instance object
    Singleton s1 = Singleton.instance;
    Singleton s2 = Singleton.instance;
    // If you don't believe me, compare the addresses of the two objects
    System.out.println(s1 == s2);// return true to the same object
}
Copy the code

That’s good, so I’m back with the question. What happens if you create two objects and the original instance object changes? Then the two instance objects are the same. Yeah, that’s right. It’s not the same object. Continue to watch the following scene!

class Test {
    // create two objects, s3 and S4, respectively. Then I change the original instance object and set it to null. What happens?
    Singleton s3 = Singleton.instance;
    // Set the original instance object to null
    Singleton.instance = null;
    Singleton s4 = Singleton.instance;
    // Compare the addresses of two objects
    System.out.println(s1 == s2);// the result is false, indicating that the same instance object is not used
}
Copy the code

Because in the above scenario, the outside world can change the original instance object, causing the creation of the instance to be inconsistent, we try to limit the outside world to change the instance. If you want to use get, you can provide a get method for your class and use it as an instance object. If an instance object is not created, the get method can be used by the outside world. Don’t forget that we have the static modifier, which can be used to specify the class name. The code is as follows, this code is the final version!

// The singleton is the same as the singleton
class Singleton {
	//1. Create an instance of the class inside the class. The statically modified object is created only once as the class loads
    private static final Singleton instance = new Singleton();
	//2. Privatize the constructor so that it cannot be called outside the class
    private Singleton(a) {}//3. Privatize this object, called through a public method
	//4. Public methods can only be called by the class because the class is static, and instances of the class must be static
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

If you are careful, you will notice that I have not only added the static modifier to my instance, but also used the final modifier. Why is that? In fact, add final so that the object will not be changed, the code is more robust!

Lazy Load

Lazy Load means that instance objects are created only when they are used. This avoids wasting resources and memory problems. In fact, lazy loading is nothing more than a choice in space for time and time for space!

Again: the implementation also leaves a problem if code is written in this class. We don’t need the instance object, it automatically creates an object when the class is loaded. It will waste resources and occupy memory. Although take up not much, but also a kind of loophole, understand!

So let’s consider the ultimate version of the singleton idea transfer process (the ultimate version is hunchman singleton), which does not support lazy loading. So how do you support lazy loading? Then we need to control the creation of objects that are not created when the class is loaded, but instead create instance objects in the GET method that are available to the outside world. First look at the code, the following method to achieve singleton mode to support lazy loading!

// Common lazy singleton (thread unsafe)
class Singleton {
    // Create an instance object
    private static Singleton instance = null;

    // Privatize constructor
    private Singleton(a) {}// Provide static get methods for creating instance objects
    public static Singleton getInstance(a) {
        // Determine if the instance object is empty. If it is empty, create the instance object and return it
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Thread-safety problem of singleton design pattern

Question: What is thread unsafe in singleton design mode?

In singleton mode, only one instance object is created, that is, the external instance objects are the same object, of course, since they are the same address! The thread insecurity of singleton design pattern is that multiple instances can be created!

Question: What thread is unsafe about the plain lazy singleton pattern? What if you make it thread-safe?

Thread-safety problem of singleton design pattern, after lazy load problem analysis, common lazy singleton pattern will have thread-safety problem.

Creating instance objects in singleton design mode is an atomic operation! Its thread is not safe, which can be explained as multiple threads create two or more instances of this singleton by seizing the time slice of the CUP in the void detection link when they concurrently access and create this singleton. Breaking the singleton design pattern singleton principle!

There are many thread-safe singleton patterns, such as the hungry singleton that introduces the thought passing process. It is inherently thread-safe. If you think about hungry singleton, I can’t create multiple instances!

Thread-safe singleton patterns, in chapter 8 of the classification analysis, I will list one by one, and all singleton patterns for write their characteristics, advantages and disadvantages and so on!

Retrofit the plain lazy singleton pattern and address thread-safety issues

If you want to change to plain lazy singletons, you have to use synchronized! There are two things you can do to solve the common lazy thread safety problem! The code is as follows:

1. Add synchronization lock to atomic operation method

// Synchronize lock lazy singleton mode (thread-safe)
class Singleton {
    // Create an instance object
    private static Singleton instance = null;

    // Privatize constructor
    private Singleton(a) {}// Get method with synchronization lock and static modifier
    public synchronized static Singleton getInstance(a) {
        // Specifically, the following are atomic operations
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

2. Add a synchronization code block to the instance object

Note: When using synchronized blocks, the parentheses cannot be this. Since we created the object using the static modifier, it is impossible for a synchronized object to synchronize an external object with a static period, because this operation is not justified. So, writing this here will float red error!

// Synchronize lock lazy singleton mode (thread-safe)
class Singleton {
    // Create an instance object
    private static Singleton instance = null;

    // Privatize constructor
    private Singleton(a) {}// Get method with synchronization lock and static modifier
    public static Singleton getInstance(a) {
        // Lock the instance object
    	synchronized(Singleton.class) {
        	if (instance == null) {
            	instance = new Singleton();
        	}
        	returninstance; }}}Copy the code

8. Classification and analysis of singleton patterns

5.1 Hunger-Hungry Singleton Mode (Recommended)

5.2 Common Lazy Singleton Mode

5.3 Synchronous Lock Lazy Singleton Mode

5.4 Synchronizing Code Blocks Lazy singleton mode

5.5 Double-Check Lock/Double-Check Singleton Mode (Recommended)

5.6 Static Inner Class/Registration Singleton Mode (Recommended)

5.7 Enumerating singleton Modes

Nine, the solution to a variety of broken singleton pattern principle method

As you all know, the reflection technique we’ve been studying is such a rascal, it’s like a big brother in front of it all the modifiers are a little brother. Can use violent reflection to break through encapsulation and modifiers.

In addition to serialization, you can write an object to a file by serialization, and then create the object by reading the file.

In the singleton design pattern, there are three singleton patterns that we recommend to use, which are both efficient and thread-safe. They are hunger-handedness singleton, double-lock check singleton and static inner class singleton. Although they are thread-safe, both can be attacked by reflection and serialization, thus breaking the singleton principle! (Let’s leave the enumeration singleton out here!)

Both reflection and serialization can break the singleton design pattern principle at this point! So what do we do?

9.1 Reflection failure singleton mode principle analysis

Reflection creates instance objects by breaking through private constructors

Reflection breaks singleton pattern principle

First, write a thread-safe singleton pattern, write any of the three, reflection break singleton principle method is the same!

// The singleton is the same as the singleton
public class Singleton {
	//1. Create an instance of the class inside the class. The statically modified object is created only once as the class loads
    private static final Singleton instance = new Singleton();
    
	//2. Privatize the constructor so that it cannot be called outside the class
    private Singleton(a) {}//3. Privatize this object, called through a public method
	//4. Public methods can only be called by the class because the class is static, and instances of the class must be static
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

Next, we use reflection to break the singleton rule!

Note: The core of creating a singleton using reflection is to invalidate its private constructor modifier and create instance objects through the constructor

public class Test {
    public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        /** * get instance object */
        Singleton instance1 = Singleton.getInstance();

        /** * reflection gets instance object */
        // Get the private constructor in the Singleton class
        Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();
        // Invalidate the private modifier and break through the private constructor
        constructor.setAccessible(true);
        // Use the class constructor to create instance objects
        Singleton instance2 = constructor.newInstance();
		
        // Check whether the object is the same object
        System.out.println(instance1 == instance2);// If the result is false, the two instance objects are not the same instance object}}Copy the code

Solves the reflection destruction singleton principle

To solve the problem of reflection breaking the singleton rule by using the constructor to create an instance object, we need to determine if the instance object has been created at the time of reflection breaking the constructor and throw an exception for it if it has been created. Simply add an instance object nuller to the private constructor. This prevents reflection technology from breaking through the singleton principle!

// The singleton is the same as the singleton
public class Singleton {
	//1. Create an instance of the class inside the class. The statically modified object is created only once as the class loads
    private static final Singleton instance = new Singleton();
    
	//2. Privatize the constructor so that it cannot be called outside the class
    private Singleton(a) {
        // Prevent reflection from breaking the singleton rule
		if (null! = instance) {// Throw an exception: failed to create the instance object
            throw new RuntimeException("Failed to create the instance object"); }}//3. Privatize this object, called through a public method
	//4. Public methods can only be called by the class because the class is static, and instances of the class must be static
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

When we try to use reflection technology to break through the singleton principle, something like this happens!

9.2 Analysis of the serialization Failure singleton Mode Principle

Serialization creates objects by writing objects to a file and reading from the file using ObjectOutputStream and ObjectInputStream streams

Serialization breaks the singleton pattern principle

First, let’s write a thread-safe singleton, so let’s stick to hunchman singleton, remember we’re going to implement the serialization interface. If serialization is not implemented, an error will be reported!

// The singleton is the same as the singleton
public class Singleton implements Serializable {
    private static final Singleton instance = new Singleton();
    
    private Singleton(a) {
        // Prevent reflection from breaking the singleton rule
		if (null! = instance) {// Throw an exception: failed to create the instance object
            throw new RuntimeException("Failed to create the instance object"); }}public static Singleton getInstance(a) {
        returninstance; }}Copy the code

Next, we break the hanchian singleton pattern with serialization.

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        /** * get instance object */
        Singleton instance1 = Singleton.getInstance();

        /** * Get instance object */ by serialization
        // Create ObjectOutputStream, FileOutputStream, and create write obj.obj
        ObjectOutputStream objectOutputStream  = new ObjectOutputStream(new FileOutputStream("obj.obj"));
        // Write instance1 to the obj.obj file
        objectOutputStream.writeObject(instance1);
        / / close the flow
        objectOutputStream.close();

        // Create ObjectInputStream, FileInputStream and specify to read obj.obj
        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("obj.obj"));
        // Read the object from the obj. Obj file and form the instance2 instance object
        Object instance2 = objectInputStream.readObject();
        / / close the flow
        objectInputStream.close();

        // Compare whether two objects are the same object
        System.out.println(instance1 == instance2);//false to prove that two objects are not the same object}}Copy the code

As you can see from the above, the way we solve reflection does not solve the serialization destruction singleton pattern.

Analyze the serialization destruction singleton principle

To solve the serialization destruction singleton principle, we need to understand the underlying principles. Then we go to the bottom of ObjectInputStream and find the Private Object readOrdinaryObject(Boolean unshared) method.

Create a readObject() return object here that can be understood as an ObjectInputStream.

Then the newInstance() method inside the red circle is used to create objects by calling the no-argument construct using the reflection technique.

Red circle on the left there is a isInstantiable () method, which is to determine if the serializable/externalizable classes can be instantiated at run time, then this method returns true.

Thus, we implement the Serializable interface, which returns true, and then creates instance objects by calling the no-argument constructor using reflection techniques, breaking the singleton principle!

Addresses the serialization break singleton principle

Now that we have seen above how serialization breaks the singleton principle, we can follow it to find a solution. For the solution, we need to define a readResolve method in the Singleton class and return the instance object in that method!

// The singleton is the same as the singleton
public class Singleton implements Serializable {
    private static final Singleton instance = new Singleton();
    
    private Singleton(a) {
        // Prevent reflection from breaking the singleton rule
		if (null! = instance) {throw new RuntimeException("Failed to create the instance object"); }}public static Singleton getInstance(a) {
        return instance;
    }
    
    // Resolve serialization break singleton pattern
    private Object readResolve(a) {
        returninstance; }}Copy the code

In this case, the serialization attack will return true for the addresses of both objects, and the result will prove that the two instances are the same. And then, how does that work? Go back to the ObjectInputStream source and find the readOrdinaryObject method, which has the following code:

The hasReadResolveMethod() method in the first red box represents the result true if a class implementing the Serializable or Externalizable interfaces contains the readResolve method.

The invokeReadResolve() method in the second red box represents the readResolve method that calls the class to be serialized using reflection techniques.

The underlying principles have also been explained, so it can be concluded that defining the readResolve method in Singleton and specifying the generation strategy for the object to return in that method prevents Singleton from being broken.

Enumerate the key underlying and attack solutions for singleton patterns

The code for enumerating a singleton pattern is just an INSTANCE. But how it is implemented, and how it avoids reflection and serialization attacks, remains to be studied.

10.1 Enumerate the singleton mode of Reflection Attack

/** * enumeration */
public enum Singleton {
    INSTANCE
}
Copy the code
public class Test {
  public static void main(String[] args)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
          InstantiationException {
    Singleton instance1 = Singleton.INSTANCE;
    Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor();

    constructor.setAccessible(true); Singleton instance2 = constructor.newInstance(); System.out.println(instance1 == instance2); }}Copy the code

An attempt to use a reflection attack results in a red error message indicating that no no-parameter construction was initialized as follows:

Then we go into the Enum source code and find that the Enum class does indeed have no no-parameter construction. So reflection cannot attack from a no-parameter constructor!

What about the parameter constructor? Is there one in the enumerated class? So I went through the source code and found that enumerations provide parameter constructs!

So let’s follow the lead and use reflection to attack the parametric structure!

public class Test {
  public static void main(String[] args)
      throws NoSuchMethodException, IllegalAccessException, InvocationTargetException,
          InstantiationException {
    Singleton instance1 = Singleton.INSTANCE;
    Constructor<Singleton> constructor = Singleton.class.getDeclaredConstructor(String.class, int.class);

    constructor.setAccessible(true); Singleton instance2 = constructor.newInstance(); System.out.println(instance1 == instance2); }}Copy the code

As a result, we get a red error message indicating that we cannot create enumerations in reflection mode, as follows:

Enumerating singleton patterns using reflection techniques is obviously not possible to create instance objects. So enumerate singleton patterns to avoid the attack of reflection technology. So how does it end up being reflective and parameterized constructs can’t create objects? Then we need to look at the source code in the newInstance() method of reflection technology. Looking at the source code, do you see why you can’t reflect enumerated classes to create objects? Because it makes a judgment on the enumeration and throws an exception if it’s an enumeration!

10.2 Serialization Attacks Enumerate the singleton mode

/** * enumeration */
public enum Singleton {
    INSTANCE
}
Copy the code
public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        Singleton          instance1          = Singleton.INSTANCE;
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("obj.obj"));

        objectOutputStream.writeObject(instance1);
        objectOutputStream.close();

        ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("obj.obj"));
        Object            instance2         = objectInputStream.readObject();

        objectInputStream.close();

        System.out.println(instance1 == instance2);//true to prove that two objects are the same object}}Copy the code

The Java specification states that each enumerated type and the enumerated variables defined are unique to the JVM, so Java makes special provisions for the serialization and deserialization of enumerated types. In serialization, Java simply prints the enumeration object’s name property into the result. In deserialization, Java finds the enumeration object by name using the valueOf() method of java.lang.enum. That is, in the case of enumerations, only the name of INSTANCE is output during serialization. When deserializing enumerations, only the name of INSTANCE is output. Therefore, the deserialized INSTANCE will be the same as the object INSTANCE that was previously serialized. So that’s what we see as true.

Xi. Table summary

Note: In addition to enumerating singleton patterns, all singleton patterns can be avoided by our intervention to avoid reflection or serialization attacks, and I have detailed explanation and underlying analysis in this article!

Singleton pattern classification Thread safe or not Whether lazy loading is supported The efficiency of Whether reflection or serialization attacks can be avoided
Hungry (recommended) is no high no
Plain slob no is high no
Synchronous lock slob is is low no
Synchronized code block lazy is is low no
Double check lock/double check lock (recommended) is is high no
Static inner class/registry (recommended) is is high no
Enumeration (best, not widely adopted) is no high Yes (naturally avoidable)

Hi, I’m Ziph. Welcome everyone to like me, leave a message! Have don’t understand can put forward oh! I’ll reply!