Small knowledge, big challenge! This article is participating in the creation activity of “Essential Tips for Programmers”.

The singleton pattern is often considered one of the simplest and best understood design patterns, but there are a lot of points to pay attention to in order to understand and use it correctly.

define

The singleton pattern provides one of the best ways 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. So we need to consider the singleton pattern when we’re talking about globally allowing only one instance in our code and frequently accessing it. For example, we often talk about user information management. In order to ensure the synchronization of user information and its frequent call characteristics, we need to use the singleton pattern to create. So what about singletons? Simply put: check whether the instance already exists in the system, if it does, return directly, if not, create and return, and privatize its own constructor, to ensure that external direct instantiation.

Several ways of writing singleton patterns

The hungry type

Hanhanism is more efficient, but due to the Classloader mechanism, the UserManager class is instantiated at class loading time, and there is a memory waste if you don’t need to access the singleton.

public class UserManager { private UserManager(){} private static UserManager mInstance = new UserManager(); public static UserManager getInstance() { return mInstance; }}Copy the code
LanHanShi

The so-called lazy is to initialize instances in the method of exposing external singletons. In the lazy writing method, it is divided into synchronous lock writing method and asynchronous lock writing method:

  1. Without synchronous lock, this writing method because there is no synchronous lock, so there are thread safety problems in multithreading, there may be multiple instances, so strictly can not be called singleton.
public class UserManager { private UserManager(){} private static UserManager mInstance; public static UserManager getInstance() { if (mInstance == null){ mInstance = new UserManager(); } return mInstance; }}Copy the code

2 add synchronous lock, although this method can better solve the problem of creating instances in multiple threads, but the efficiency is very low, because in fact only the first nulltime need to lock, as long as it is initialized once is not the need for synchronization lock mechanism.

public class UserManager { private UserManager(){} private static UserManager mInstance; public static synchronized UserManager getInstance() { if (mInstance == null){ mInstance = new UserManager(); } return mInstance; }}Copy the code
Double check lock check mode

This approach is the most common singleton we use, considering both thread safety and efficiency. In fact, called this way for double check lock may be easy to mislead, will think is two locks, personal feeling called “double empty” or “double check + lock” more appropriate, in a word to understand its writing. In the JVM mInstance = new UserManager() is split into three unit operations:

  1. Allocates memory addresses for power.
  2. Create instance objects.
  3. Assigns the created object to the memory address just allocated (the object is not empty until this happens).

Without double short call, if there was only one short call, there would be a scenario like this: When a thread enters the method body, mInstance becomes empty, so instantiate it. When executing the operation 1 or 2 above, another thread also enters the method body. However, since the operation 3 of the first thread has not completed, mInstance remains empty, so the latter thread will perform another initialization. Two instances are created when both threads have completed access.

public class UserManager { private UserManager(){} private static volatile UserManager mInstance; public static UserManager getInstance() { if (mInstance == null){ synchronized (UserManager.class){ if (mInstance == null){ mInstance = new UserManager(); } } } return mInstance; }}Copy the code
Static inner class mode

Because of the class loading mechanism, even if the UserManger class is loaded, its inner class is not initialized. It is initialized only when an inner class method is called, resulting in lazy load. A lot of people recommend this.

public class UserManager { private UserManager(){} static class User{ private static final UserManager USER_MANAGER = new UserManager(); } public static UserManager getInstance() { return User.USER_MANAGER; }}Copy the code
The enumeration

This approach, advocated by Effective Java author Josh Bloch, not only avoids multithreaded synchronization issues, but also automatically supports serialization, prevents deserialization from recreating new objects, and absolutely prevents multiple instantiations. However, since the enum feature was added later in JDK1.5, writing this way is somewhat unfamiliar and rarely used in practice. Private constructors cannot be called through Reflection Attack.

public enum UserManager{
    USER_INSTANCE;
}
Copy the code

conclusion

  • Only one instance of a singleton class can exist globally.
  • A singleton class must privatize its constructor so that it cannot be instantiated directly from outside.
  • A singleton must create its own unique instance and provide a way to get it.