java design pattern - singleton pattern

java design pattern - singleton pattern

1, Overview

The definition of singleton pattern is to ensure that there is only one instance of a class and provide a global access point. It belongs to the creative pattern in the three categories of design patterns.
The singleton mode has three typical characteristics:

There is only one instance.
Self instantiation.
Provides a global access point.

2, Advantages and disadvantages

Advantages: since the singleton mode generates only one instance, it can save system resources, reduce performance overhead, improve system efficiency, and strictly control customers' access to it.
Disadvantages: because there is only one instance in the system, the responsibility of the singleton class is too heavy, which violates the "single responsibility principle", and there is no abstract class, so it is difficult to expand.

3, Common implementation methods

There are five common implementation methods of singleton mode: hungry, lazy, double detection lock, static internal class and enumeration singleton. Among these five ways, hungry and lazy are the most common. The implementation methods of these five methods are listed below:

  1. Hungry Chinese style: thread safety and high calling efficiency. However, you cannot delay loading. Example:

    public class HungryMode {
    /**
     * Thread safe
     * Class is initialized, this object is loaded immediately
     */
    private static HungryMode hungry = new HungryMode();
    
    private HungryMode() {
    
    }
    
    /**
     * Method does not add synchronization block, so it is efficient
     * @return
     */
    public static HungryMode getInstance() {
        return hungry;
    }
    }
    

    Because the object is created when the class is loaded in this mode, the speed of loading the class is relatively slow, but the speed of obtaining the object is relatively fast and thread safe.

  2. Lazy: thread unsafe. Example:

public class Lazymode {
    /**
     * Thread unsafe
     */
    private static Lazymode lazymode = null;

    private Lazymode() {
    }

    /**
     * Load objects at runtime
     *
     * @return
     */
    public static Lazymode getInstance() {
        if (lazymode == null) {
            lazymode = new Lazymode();
        }
        return lazymode;
    }
}

Because this mode loads objects at runtime, it is faster to load classes, but the object acquisition speed is relatively slow and the thread is unsafe. If you want thread safety, you can add the synchronized keyword, but it will pay a heavy cost of efficiency.

  1. Lazy type (double synchronous lock)
public class LazyModeSync {
   private static volatile LazyModeSync instance = null;

   private LazyModeSync() {
   }

   /**
    * Load objects at runtime
    * @return
    */
   public static LazyModeSync getInstance() {
       if (instance == null) {
           synchronized (LazyModeSync.class) {
               if (instance == null) {
                   instance = new LazyModeSync();
               }
           }
       }
       return instance;
   }
}
  1. Dual detection lock supplement:
    Why is it necessary to judge the space again after adding the synchronization lock?
    Because if there is no second air judgment, the following situations may occur:
Thread 1Thread 2
instance is determined to be empty for the first timeinstance is determined to be empty for the first time
Acquire lockWait 1 to release the lock
Initialize instance-
Obtain the lock and initialize instance

In this way, instance will be initialized twice, so it is necessary to perform secondary null determination after obtaining the lock.
6. Why use volatile keyword?
Because there may be instruction rearrangement during java initialization

Instruction rearrangement:
Generally speaking, initialization is not an atomic operation, but is divided into three steps:

Open up the space required for objects in the heap and allocate addresses
 Initialize according to the initialization order of class loading
 Returns the memory address to the reference variable in the stack
 because Java The memory model allows "write out of order". Some compilers may reorder 2 and 3 in the above steps for performance reasons

Open up the space required for objects in the heap and allocate addresses
 Return the memory address to the reference variable in the stack (at this time, the variable is no longer null,But variables are not initialized)
Initialize according to the initialization order of class loading
Thread 1Thread 2
For the first detection, instance is empty
Acquire lock
Detect again, and instance is empty-
Allocate memory space in the heap
instance points to the allocated memory space
For the first detection, instance is not empty
Access instance (at this time, the object is still initialized)

After adding volatile keyword modification, instruction rearrangement will be disabled, so as to ensure thread synchronization.

4, Common application scenarios
  • Site counters.
  • The class in the project used to read the configuration file.
  • Database connection pool. Because the database connection pool is a database resource.
  • In Spring, each Bean is singleton by default, which is convenient for the Spring container to manage.
  • Application in Servlet

Keywords: Java Algorithm Singleton pattern

Added by pahikua on Sun, 02 Jan 2022 06:25:15 +0200