Patterns are independent of language.

First, the pattern origin of singleton

Multiple threads must operate on the same object to ensure the uniqueness of the object.

How to solve it?

The instantiation process is instantiated only once.

Four principles of the singleton pattern

1. Privatize constructors 2. Return instances with static methods or enumerations 3. Ensure there is only one instance, especially in multithreaded environments 4. Ensure that objects are not rebuilt when deserializing

Our common singleton patterns are:

  • The hungry mode
  • Lazy mode
  • Dual retrieval mode
  • Static inner class schema
  • Enumeration mode

2. Classification of singleton patterns

1. Hungry

public class HungrySingleton { /** * 1. */ private static HungrySingleton instance = new HungrySingleton(); Private HungrySingleton(){} /** * 3. Public static HungrySingleton getInstance(){return instance; } public static void main(String[] args) { for (int i = 0; i < 50; i++) { new Thread(() ->{ System.out.println(HungrySingleton.getInstance()); }).start(); }}}Copy the code

Safety of hungry mode:

  • The HungrySingleton class is instantiated when it is loaded by the class loader, so only once, in exchange for time, is the thread safe.
  • The efficiency problem
  • There is no lazy loading, and if it is not used after creation, it takes up memory and affects performance

2. LanHanShi

public class LazySingleton { /** * 1. */ private static LazySingleton instance = null; Private LazySingleton(){}; Public static LazySingleton getInstance(){if(objects.isnull (instance)){instance = new LazySingleton(); } return instance; } public static void main(String[] args) {** ** for (int I = 0; i <10000; i++) { new Thread(() ->{ System.out.println(LazySingleton.getInstance()); }).start(); }}}Copy the code

Lazy mode security: LazySingleton creates objects only after methods are called, trading time for space, which is risky in multithreaded environments.

3. Double Check -Lock

public class DCLSingleton { /** * 1. */ private static DCLSingleton instance = null; / * * * 2. The constructor private * / private DCLSingleton () {} / * * * 3. Public static DCLSingleton getInstance(){if(objects.isnull (instance)){synchronized (DCLSingleton.class){ if(Objects.isNull(instance)){ instance = new DCLSingleton(); } } } return instance; }}Copy the code

The code above: In the method of getting the instance object getInstance(), we first determine if instance is empty, if so, lock dclSingleton.class, check again if instance is empty, and create an instance of DCLSingleton if it is still empty.

If we have two threads (thread A, thread B) calling getInstance () at the same time, they will find instance == NULL and lock dclSingleton.class. Another thread will be in the wait state (if thread B); Thread A will create A DCLSingleton instance and release the lock. After the lock is released, thread B will wake up and try to lock again. Then the lock can be successfully added. Thread B will not create another DCLSingleton instance since it has already created one.

It all looks perfect, but the getInstance () method isn’t perfect. What’s wrong with the new operation?

  1. Allocate a block of memory M
  2. Initialize the DCLSingleton object on memory M;
  3. M’s address is then assigned to the instance variable

But the possible execution order after JVM optimization is as follows: 1. Allocate a memory M; 3. Finally, initialize the DCLSingleton object in memory.

What problems do optimizations cause? Let’s assume that thread A executes the getInstance () method first, and when instruction 2 is finished, A thread switch happens, switching thread B; If thread B also executes the getInstance () method, then thread B will find instance! = null, so instance is returned directly, and the instance is not initialized. If we call the member variable of instance, we may raise the null-pointer exception.

4. Dual retrieval (DCL) + volatile

public class DCLSingleton { /** * 1. */ private volatile static DCLSingleton instance = null; / * * * 2. The constructor private * / private DCLSingleton () {} / * * * 3. Public static DCLSingleton getInstance(){if(objects.isnull (instance)){synchronized (DCLSingleton.class){ if(Objects.isNull(instance)){ instance = new DCLSingleton(); } } } return instance; }}Copy the code

What volatile does:

1. Ensure visibility:

Changes to a shared variable are immediately perceived by other threads, so that volatile ensures that instance is read in main memory every time, ensuring instance consistency

2. Ensure order:

11. as-if-serial: Reordering does not affect the execution of a program on a single thread, and does not affect multithreading as volatile: Code before volatile cannot be repositioned after it and code after volatile cannot be repositioned before it without changing

5. Static inner class schema

public class Singleton { /** * 1. Private Singleton(){} /** * 2. When we define a static inner class * *, we do not declare instance variables in member variables, */ private static class SingletonHolder{private static Singleton instance = new Singleton(); Public static Singleton getInstance(){return singletonholder.instance; }}Copy the code

Advantages of static inner classes:

When the external class is loaded, it does not need to load the inner class immediately. If the inner class is not loaded, instance will not be initialized. Therefore, it does not need to load the SingleHolder when the Singleton is loaded for the first time. The first call to getInstance () causes the JVM to load the SingletonHolder class. This method not only ensures thread-safety, but also ensures uniqueness of singletons, while delaying the instantiation of singletons.

Supplementary Knowledge When initializing a class, the initialization of its parent class is triggered first if the parent class has not been initialized.

  1. When the bytecode instructions new, getstatic, setstatic, or Invokestatic are encountered, the corresponding Java code scenario is: New when a keyword or an instantiated object, when a static field is read or set (except for final modifiers, where the result has been put into the constant pool at compile time), when a static method of a class is called
  2. When a java.lang.Reflect method is used to make a reflection call to a class, if the class is not already initialized, it needs to call its initialization method first.
  3. When initializing a class, the initialization of its parent class is triggered first if the parent class has not been initialized.
  4. When the virtual machine starts, the user needs to specify a main class (the class containing the main() method) to execute, and the virtual machine initializes this class first.
  5. The dynamic language support such as using JDK 1.7, if a Java lang. Invoke. The final analytical results REF_getStatic MethodHandle instance, REF_putStatic, REF_invokeStatic method handles, If the class to which the method handle corresponds has not been initialized, it 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. Static inner classes are among the passive references.

So, is a static inner class singleton the perfect singleton pattern? In fact, static inner class also has a fatal disadvantage, is the problem of parameter passing, because it is in the form of static inner class to create a singleton, so external parameters can not be passed in, such as Context parameters, so we can create a singleton, static inner class and DCL mode of their own choice.

6. Enumeration mode

public class EnumSingleton { /** * 1. Private enumleton (){} private enum SingletonHolder{// Create an enumeration object that is inherently a singleton; private EnumSingleton instance; SingletonHolder(){ instance = new EnumSingleton(); } public EnumSingleton getInstance(){ return instance; Public static enumleton getInstance(){return;}} public static enumleton getInstance(){return SingletonHolder.INSTANCE.instance; }}Copy the code