This is the second day of my participation in Gwen Challenge

An overview,

Singleton pattern is an interview will often be asked a question, there are a lot of the article introduces the realization of the singleton pattern, this article is also reference for those good articles to make a summary, through their understanding in the learning process for recording, and complement some content, on the one hand, consolidate their learning content, on the other hand, hoping to provide some help to other students.

This article mainly introduces the singleton pattern from the following aspects:

  1. What is the singleton pattern
  2. Usage scenarios for the singleton pattern
  3. Advantages and disadvantages of the singleton pattern
  4. Implementation of singleton pattern (emphasis)
  5. conclusion

What is the singleton pattern

The 23 design patterns can be divided into three categories: creative patterns, behavioral patterns, and structural patterns. The singleton is one of the simplest design patterns: it involves only one class, ensures that there is only one instance of a class in the system, and provides a global access point. Many times the entire system only needs to have one global object, which helps us coordinate the behavior of the system as a whole.

3. Usage scenarios of singleton pattern

1. Logging classes

Logging classes are typically implemented as singletons and provide global logging access points in all application components without the need to create objects each time a logging operation is performed.

2. Configuration classes

The configuration design for singleton implementation class, for instance in a server applications, the server configuration information stored in a file, the configuration data is read by a singleton, and other objects in the service process to get these configuration of information through the singleton, this approach simplifies the configuration management in a complex environment.

3

Suppose we design an application with a factory to generate new objects with ids (Acount, Customer, Site, Address objects) in a multi-threaded environment. If the factory is instantiated twice in two different threads, then two different objects may have two overlapping ids. We can avoid this problem if we implement the factory as a singleton, and it is a common practice to combine abstract factory or factory methods with singleton design patterns.

Classes that access resources in shared mode

For example, the site’s counters are generally implemented in singleton mode. If you have multiple counters, each user’s access will refresh the counter value, so that your real count value is difficult to synchronize. However, this problem does not exist if implemented in a singleton pattern, and thread safety issues can also be avoided.

5. Bean instances created in Spring are singleton by default.

Applicable scenarios:

  • An environment where a unique sequence needs to be generated
  • Objects that need to be frequently instantiated and then destroyed.
  • An object that takes too much time or resources to create but is often used.
  • An environment that facilitates communication between resources

Advantages and disadvantages of singleton mode

Advantages:

  • There is only one object in memory to save memory space.

  • Avoiding frequent creation and destruction of objects reduces GC effort and improves performance;

  • Avoid multiple occupation of shared resources and simplify access;

  • Provide a global access point for the entire system.

Disadvantages:

  • Does not apply to objects that change frequently;

  • Abuse of singletons will bring some negative problems, for example, in order to save resources, the database connection pool object is designed as a singleton class, may lead to too many programs sharing the connection pool object and the connection pool overflow;

  • If the instantiated object is not used for a long time, the system will consider the object as garbage and collect it, which may lead to the loss of the object state.

5. Implementation of singleton Pattern (key points)

The steps to implement the singleton pattern are as follows:

  1. Privatize constructors to prevent external classes from creating objects through new
  2. Define a private static variable that holds its own type
  3. Provide a static public method to retrieve instances
  4. If you implement the serialization interface you need to ensure that deserialization does not recreate the object

1, hungry, thread safety

Hunchman singleton mode, as the name implies, creates objects as soon as the class is loaded. This method is more common, but it is easy to generate garbage objects and waste memory space.

Advantages: Thread-safe, no lock, high execution efficiency Disadvantages: Not lazy loading, class initialization, waste memory space

Lazy loading: Creating objects as they are used

How do hungry singletons keep threads safe? It is based on the class loading mechanism to avoid multithreading synchronization problems, but if the class is loaded by different class loaders will create different instances.

Code implementation, and the use of reflection destruction singletons:

/** * single example test **@className: Singleton
 * @date: 2021/6/7 14:32
 */
public class Singleton  {
    // 1
    private Singleton(a){}
    // define a static variable that points to its own type
    private final static Singleton instance = new Singleton();
    // 3. Provide a public method to obtain an instance externally
    public static Singleton getInstance(a) {
        returninstance; }}Copy the code

To use the reflection destruction singleton, the code is as follows:


public class Test {

    public static void main(String[] args) throws Exception{
        // Use reflection to destroy singletons
        // Get the empty parameter constructor
        Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(null);
        // Set mandatory access
        declaredConstructor.setAccessible(true);
        // Create an instance
        Singleton singleton = declaredConstructor.newInstance();
        System.out.println("Reflection created instance" + singleton);
        System.out.println("Normally created instance" + Singleton.getInstance());
        System.out.println("Normally created instance"+ Singleton.getInstance()); }}Copy the code

The following output is displayed:

Reflection to create an instance of the com. Example. Spring. Demo. Single. The Singleton @ 6267 c3bb normal to create an instance of the com. Example. Spring. The demo. Single. Ddba Singleton @ 533 Normal to create an instance of the com. Example. Spring. Demo. Single. Ddba Singleton @ 533Copy the code

2, lazy, thread is not safe

This approach works fine for single threads. There is no guarantee of singletons for multiple threads. It is listed here for comparison with the singletons that use locks to ensure thread safety.

Advantages: Lazy loading Disadvantages: Thread insecurity

The code implementation is as follows:


/** * threads are not safe **@className: Singleton
 * @date: 2021/6/7 14:32
 */
public class Singleton  {
    // 1
    private Singleton(a){}// define a static variable that points to its own type
    private static Singleton instance;
    // 3. Provide a public method to obtain an instance externally
    public static Singleton getInstance(a) {
        // Create the object when it is null
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

Using multithreading to destroy singletons, the test code is as follows:

public class Test {

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) {
            new Thread(() -> {
                System.out.println("Singleton created by multiple threads:"+ Singleton.getInstance()); }).start(); }}}Copy the code

The following output is displayed:

Multithreaded create Singleton: com. Example. Spring. Demo. Single. The Singleton @ 18396 bd5 multithreaded create Singleton: Com. Example. Spring. Demo. Single. Singleton @ 7 f23db98 multithreaded create Singleton: com. Example. Spring. The demo. Single. Singleton @ 5000 d44Copy the code

3, lazy, thread safety

How do lazy singletons keep threads safe? Synchronized keyword can be added to methods or code blocks to ensure thread safety. Synchronized can be added to methods as well as code blocks. The problem is that every time getInstance is called to obtain an instance, the lock needs to be added and released. This is very performance critical.

Advantages: lazy loading, thread safety Disadvantages: low efficiency

The code implementation is as follows:

Synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized synchronized@className: Singleton
 * @date: 2021/6/7 14:32
 */
public class Singleton  {
    // 1
    private Singleton(a){}// define a static variable that points to its own type
    private static Singleton instance;
    // 3. Provide a public method to obtain an instance externally
    public synchronized static Singleton getInstance(a) {
        if (instance == null) {
            instance = new Singleton();
        }
        returninstance; }}Copy the code

4. Double-checked locking (DCL)

The implementation code is as follows:


/** * double-checked locking **@className: Singleton
 * @date: 2021/6/7 14:32
 */
public class Singleton {
    // 1
    private Singleton(a) {}// define a static variable that points to its own type
    private volatile static Singleton instance;

    // 3. Provide a public method to obtain an instance externally
    public synchronized static Singleton getInstance(a) {
        // First check for null
        if (instance == null) {
            // Use synchronized locking
            synchronized (Singleton.class) {
                // The second check is for null
                if (instance == null) {
                    Object creation with the new keyword is not an atomic operation
                    instance = newSingleton(); }}}returninstance; }}Copy the code

Advantages: lazy loading, thread safety, high efficiency Disadvantages: complex implementation

In fact, the first judgment is very simple. If the instance already exists, the synchronization operation is no longer needed, but the instance is directly returned. If it is not created, the synchronized block will enter. The purpose of the synchronized block is the same as before, the purpose is to prevent multiple threads calls at the same time, lead to generate multiple instances of a synchronized block, each time only one thread calls visit synchronized block content, when the first to get the lock after call for instance, the instance is created, after all calls will not enter the synchronized block, The singleton is returned directly on the first judgment.

About internal function of the second empty judgment, when multiple threads together to lock position, lock contention, one thread lock, if is the first time to enter is null, the will to create singleton object, after the completion of the lock is released, other threads will be empty after acquiring a lock judgment interception, returned directly created a singleton.

One of the most critical points is the use of the volatile keyword. You can search for the volatile keyword in detail. There are many well-written articles that are not covered in detail here. Two important features of volatile use in double-checked locks are visibility and instruction reordering prohibition

Why volatile here?

This is because the new keyword is not an atomic operation. Creating an object goes through the following steps:

  1. Open up memory space in heap memory
  2. Call the constructor to initialize the object
  3. Reference variables point to heap memory space

The corresponding bytecode instructions are as follows:

To improve performance, compilers and processors often reorder instructions in a given code execution order. The process from source code to final execution instructions goes as follows:

Graph LR [source] -- > B ([compiler optimization reorder]) - > C ([instruction-level parallel reorder]) - > D (reorder] [memory system) - > E [final instruction execution sequence]

So after instruction reordering, the order of execution for creating objects might be 1, 2, 3, or 1, 3, 2, so when a thread runs instructions 1, 3, 2 out of order, the reference variable points to the heap space, the object is not null, but it is not initialized, If (instance == NULL) the first instance of getInstance is not nulll, then an exception will occur. This is the famous DCL failure problem.

When we add volatile to reference variables, we avoid this problem by adding a memory barrier before and after object instructions are created to prevent instruction reordering, and changes to volatile variables are visible to any other thread.

5. Static inner classes

The code implementation is as follows:


/** * Static inner class implements singleton **@className: Singleton
 * @date: 2021/6/7 14:32
 */
public class Singleton {
    // 1
    private Singleton(a) {}// 2. Provide a public method to obtain the instance
    public static Singleton getInstance(a) {
        return InnerClass.INSTANCE;
    }

    // Define a static inner class
    private static class InnerClass{
        private final static Singleton INSTANCE = newSingleton(); }}Copy the code

Advantages: lazy loading, thread safety, high efficiency, simple implementation

How does a static inner class singleton achieve lazy loading? First, let’s look at the loading timing of the class.

The virtual machine specification requires that classes be initialized immediately in only five cases (loading, validation, and preparation need to begin before then) :

  1. encounternew,getstatic,putstatic,invokestaticWhen these four bytecode instructions. The most common Java code scenario for generating these four instructions is: usenewWhen the keyword instantiates an object, when static fields of a class are read or set (except for final, which are constants and have been put into the constant pool at compile time), and when static methods of a class are called.

  2. usejava.lang.reflectWhen a package method makes a reflection call to a class.

  3. When initializing a class, if the parent class has not been initialized, the initialization of the parent class must be triggered first.

  4. When the virtual machine starts, the user needs to specify a primary class (the one containing main()) to execute, and the virtual machine initializes this primary class first.

  5. When using dynamic language support in JDK 1.7, if ajava.lang.invoke.MethodHandle The final parse result of the instance is REF_getStatic,REF_putStatic,REF_invokeStaticThe class to which the method handle corresponds needs to be initialized first.

These five cases are called active references to a class. Note that the qualifier used in the VIRTUAL Machine Specification is “have and only”, so all other reference classes do not initialize the class and are called passive references. A static inner class is a passive reference case.

When the getInstance() method is called, the InnerClass replaces the symbolic reference with a direct reference in the Singleton’s runtime constant pool, and the static object INSTANCE is actually created and then returned by the getInstance() method. This is the same as the hungry and hungry model.

How can INSTANCE be thread-safe during creation? In Understanding the JAVA Virtual Machine, there is a quote:

The virtual machine ensures that a class’s < Clinit >() methods are locked and synchronized correctly in a multithreaded environment. If multiple threads initialize a class at the same time, only one thread will execute the class’s < Clinit >() methods, and all the other threads will block and wait. Until the active thread completes executing the

() method. Multiple processes can block if there are lengthy operations in a class’s

() method. (Note that other threads can block, but if the

() method is executed, other threads will not wake up and re-enter the

() method. A type is initialized only once under the same loader. In practice, this blocking is often hidden.



From the above analysis, it can be seen that INSTANCE is thread-safe in the creation process, so the singleton in the form of static inner class can ensure thread-safe and uniqueness of singleton, but also delay the instantiation of singleton.

6. Enumerate singletons

The code implementation is as follows:

/** * enumeration implements singleton **@className: Singleton
 * @date: 2021/6/7 14:32
 */
public enum Singleton {
    INSTANCE;
    public void doSomething(String str) { System.out.println(str); }}Copy the code

Advantages: Simple, efficient, thread-safe, can avoid destroying enumeration singletons through reflection

Enumerations can have fields and methods just like normal classes in Java, and enumeration instance creation is thread-safe. In any case, it is a singleton that can be called directly to get the instance as follows:

Singleton singleton = Singleton.INSTANCE;
Copy the code

Decompile the enumeration class using the following command

javap Singleton.class
Copy the code

You get the following

Compiled from "Singleton.java"
public final class com.spring.demo.singleton.Singleton extends java.lang.Enum<com.spring.demo.singleton.Singleton> {
  public static final com.spring.demo.singleton.Singleton INSTANCE;
  public static com.spring.demo.singleton.Singleton[] values();
  public static com.spring.demo.singleton.Singleton valueOf(java.lang.String);
  public void doSomething(java.lang.String);
  static {};
}

Copy the code

As you can see from the decompile of enumerations, INSTANCE is modified static final, so it can be called directly by the class name, and instances of objects are created in static code blocks, because properties of static type are initialized after the class is loaded. Static resources are initialized when a Java class is actually used for the first time, Java classes are loaded, and the initialization process is thread-safe, so creating an enum type is thread-safe.

The enumeration is broken by reflection, and the code is as follows:

public class Test {
    public static void main(String[] args) throws Exception {
        Singleton singleton = Singleton.INSTANCE;
        singleton.doSomething("hello enum");

        // Try to use reflection to destroy singletons
        // Enumeration classes have no empty parameter constructor. After decompilation, you can see that enumeration has a two-parameter constructor
        Constructor<Singleton> declaredConstructor = Singleton.class.getDeclaredConstructor(String.class, int.class);
        // Set mandatory access
        declaredConstructor.setAccessible(true);
        // Create an instance, an error is reported because you cannot create an instance of an enumerated through reflectionSingleton enumSingleton = declaredConstructor.newInstance(); System.out.println(enumSingleton); }}Copy the code

The following error is reported:

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.base/java.lang.reflect.Constructor.newInstanceWithCaller(Constructor.java: 492).at java.base/java.lang.reflect.Constructor.newInstance(Constructor.java: 480).at com.spring.demo.singleton.Test.main(Test.java24) :Copy the code

View the newInstance() method for creating instances of reflection as follows:

So you cannot create instances of enumerations through reflection.

Six, summarized

In Java, if a Singleton class implements the java.io.Serializable interface, multiple instances of the Singleton class are created when the Singleton is serialized and then deserialized multiple times. To avoid this, the readResolve method should be implemented. See Serializable () and readResolve Method () in Javadocs.


public class Singleton implements Serializable {
    // 1
    private Singleton(a) {}// 2. Provide a public method to obtain the instance
    public static Singleton getInstance(a) {
        return InnerClass.instance;
    }

    // Define a static inner class
    private static class InnerClass{
        private final static Singleton instance = new Singleton();
    }


    // This method is called immediately after the object is deserialized, and we override it to return the singleton.
    protected Object readResolve(a) {
            returngetInstance(); }}Copy the code

Some points to note when using the singleton design pattern:

  • Multithreading – Be careful when you must use singletons in multithreaded applications.
  • Serialization – When singletons implement the Serializable interface, they must implement the readResolve method to avoid having two different objects.
  • Class loaders – If the Singleton class was loaded by 2 different class loaders, we would have 2 different classes, one for each class.
  • Global access point represented by class name – gets a singleton instance using the class name. This is an easy way to access it, but it’s not very flexible. If we need to replace the Sigleton class, all references in the code should change accordingly.

This paper briefly introduces several implementations of the singleton design pattern, except enumeration singleton, all other implementations can destroy the singleton pattern through reflection. Enumeration is recommended to implement the singleton pattern in Effective Java. Which singleton implementation to use in practical scenarios should be chosen according to your own situation. A better approach is one that fits the current scenario.

Refer to the article

Blog.csdn.net/mnb65482/ar…

www.oodesign.com/singleton-p…