Make sure there is only one instance of a class, and instantiate it yourself and make it available to the entire system.

Scenarios used by the singleton pattern

Ensure that a class has only one object to avoid creating multiple objects that consume too many resources, or that there should be only one object of a certain type. For example, creating an object consumes too many resources. If you want to access IO and database resources, you need to consider using the singleton pattern.

UML class diagram for the singleton pattern

There are several key points to realize the singleton pattern:

  • Constructors are not open to the public and are generally Private
  • Returns a singleton via a static method or enumeration
  • Ensure that there is one and only one object for a singleton class, especially in a multithreaded environment
  • Ensure that the singleton does not rebuild the object during deserialization

The sample

Example class diagram:Sample implementation code

Public class Staff{public void work(){// work}}Copy the code
Public class VP extends Staff{@override public void work(){public void work(){Copy the code
Public class extends Staff{private static final CEO = new CEO(); private static final CEO = new CEO(); Private constructor private CEO(){} public static CEO getInstance(){return mCeo; } @override public void work(){Copy the code
Public class Company{private List<Staff> allstaff = new ArrayList<>(); public void addStaff(Staff staff){ allStaffs.add(staff); } public void showAllStaffs(){ for (Staff staff : allStaffs){ System.out.println("Obj:" + staff.toString()); }}}Copy the code
Public class Client{public static void main(String[] args){Company cp = new Company(); Staff ceo1 = CEO.getInstance(); Staff ceo2 = CEO.getInstance(); Staff vp1 = new VP(); Staff vp2 = new VP(); Staff staff1 = new Staff(); Staff staff2 = new Staff(); cp.addStaff(ceo1); cp.addStaff(ceo2); cp.addStaff(vp1); cp.addStaff(vp2); cp.addStaff(staff1); cp.addStaff(staff2); cp.showAllStaffs(); }}Copy the code
Results:  Obj:com.zxf.srp.company.CEO@45ee12a7 Obj:com.zxf.srp.company.CEO@45ee12a7 Obj:com.zxf.srp.company.VP@330bedb4 Obj:com.zxf.srp.company.VP@2503dbd3 Obj:com.zxf.srp.company.Staff@4b67cf4d Obj:com.zxf.srp.company.Staff@7ea987acCopy the code

As you can see from the above code, the CEO class cannot construct an object from the new form. The CEO object can only be obtained by the ceo.getInstance () function. The CEO object is static and initialized when declared, which ensures that the CEO object is unique. The CEO output object is the same twice, but VP and Staff object are different. The core of this implementation is to privatize the constructor of the class, so that external programs cannot construct the CEO object through the constructor, while the CEO class returns a static object through a static method.

Other implementations of the singleton pattern

  • Lazy mode

GetInstance is initialized when the user first calls getInstance. The lazy singleton is initialized when the user first calls getInstance. The lazy singleton is implemented as follows:

public class Singleton{ private static Singleton mInstance; private Singleton; public static synchronized Singleton getInstance(){ if(mInstance == null){ mInstance = new Singleton(); } return mInstance; }}Copy the code

GetInstance () adds the synchronized keyword to the getInstance() method. This is a way of ensuring the uniqueness of singletons in multithreaded situations. However, there is a problem with this. Synchronization occurs every time the getInstance() method is called, which consumes unnecessary resources and is the biggest problem with the lazy singleton pattern. Lazy mode is instantiated only when it is used, saving resources to some extent. The disadvantage is that it needs to be initialized in time when loading for the first time, which is a little slow. The biggest problem is that every time getInstance() is called, it is synchronized, causing unnecessary overhead.

  • Double Check Lock(DCL) Double Check Lock

The DCL approach to implement the singleton pattern has the advantage of being able to initialize instances only when needed, ensuring thread-safety, and singleton initialization calls to getInstance without synchronizing locks. The code is as follows:

public class Singleton{ private static Singleton mInstance; private Singleton(){} public static Singleton getInstance(){ if(mInstance == null){ synchronized(Singleton.class){ if(mInstance == null){ mInstance = new Singleton(); } } } return mInstance; }}Copy the code

As you can see from the above code, getInstance() punches mInstance twice, the first to avoid unnecessary synchronization, and the second to create instances if null. Let’s say thread A executes mInstance = new Singleton(), which looks like A single statement, but is not an atomic operation. This code will eventually be compiled into multiple assembly instructions. It does three things roughly:

  1. Allocate memory to the instance of Singleton
  2. Call the Singleton constructor to initialize the member field
  3. Point the sInstance object to the allocated memory space

However, since the Java compiler allows processor out-of-order execution, and the Cche and registers in the JVM prior to JDK1.5 allow out-of-order execution, the second and third configurations above are not guaranteed. That is, the execution order may be 1-2-3 or 1-3-2. If it is the latter and it finishes at 3, Before execution, sInstance 2 is cut to thread B. At this point, since thread A has executed the third mInstance, the mInstance is non-empty, and all threads B directly execute mInstance, which leads to an error when using it. This is DCL failure.

SUN has been aware of this issue since JDK1.5 and tweaked the JVM to specify the volatile keyword so that in JDK1.5 or later, You only need to change the definition of mInstance to private volatile static Singleton mInstance to ensure that the mInstance object is read from main memory each time, ensuring the uniqueness of the object. The addition of volatile affects the performance of the program somewhat, but it is worth sacrificing performance to ensure that the program is correct.

Advantages of DCL: High resource utilization. Disadvantages: slower first loading, occasional failures due to the Java memory model, and some drawbacks in high concurrency environments. The DCL pattern is the most used singleton implementation. It only instantiates singletons when needed and can prove the uniqueness of objects in most scenarios.

  • Static inner class singleton pattern

DCL though to a certain extent, solve the resource consumption, redundant synchronization, thread safe, but it will still appear in some cases, solve the problem of the problem is called double-checked locking (DCL) failure, at the end of the book Java concurrent programming practice talked about this problem, and points out that the “optimization” is ugly, Deprecated, the following code is recommended instead.

public class Singleton{ private Singleton{} public static Singleton getInstance(){ return SingletonHolder.mInstance(); } private static class mInstance = new Singleton(); private static final Singleton mInstance = new Singleton(); }}Copy the code

MInstance is not initialized when the Singleton class is loaded for the first time. MInstance is initialized only when the Singleton getInstance() is called for the first time. Therefore, The first call to getInstance() causes the virtual machine to load the SingletonHolder class, which not only ensures thread-safety, but also ensures singleton uniqueness and delays singleton initialization, so this is the recommended singleton implementation.

  • Enumerated the singleton

The singleton implementation described earlier is either a little more cumbersome or can be problematic in some cases. Enumerating singletons is a simpler way to do this:

public enum SingletonEnum{ INSTANCE; public void doSomething(){ System.out.println("do sth."); }}Copy the code

The simplicity of writing is one of the biggest advantages of enumeration singletons. Enumerations in Java are the same as normal classes in that they can not only have fields but also have their own methods. Most importantly, an enumeration instance is thread-safe by default and is a singleton in any case. In the above singleton implementations, they recreate the object in the case of deserialization.

We know through the serialization can speak a singleton instance of the object to disk, and then read back, leading to more effective for an instance, even if the constructor is private, deserialization can still through the special way to create a new instance of a class, is equivalent to calling the class constructor, deserialization provides a very special hooks, Class has a private readResolve() function that gives developers control over deserialization of objects. In several examples, for example, the readResolve function must be added to prevent singletons from being regenerated when deserialized.

public class Singleton{ private Singleton{} public static Singleton getInstance(){ return SingletonHolder.mInstance(); } private static class mInstance = new Singleton(); private static final Singleton mInstance = new Singleton(); } private Object readResolve() throws ObjectStreamException{ return mInstance; }}Copy the code

That is, returning a singleton in the readResolve method rather than regenerating an object, which is not an issue for enumerations because it does not regenerate a new instance even if deserialized. Two other points to note:

  1. If a field type in a Serializable class is not a Java built-in type, that field type also needs to implement the Serializable interface
  2. If you adjusted the serializable class, the internal structure of new fields, such as removing a field, but not modify SerialVersionID, then the result will be a “Java. IO. JnvalidClassException” abnormal or lead to a property of 0 or null, The best solution at this point is to set SerialVersionID to 0L directly, so that even if we change the internal structure of the class, we will not throw an exception for deserialization, except that those fields will be 0 or NULL.
  • Use containers to implement the singleton pattern

The specific code is as follows:

public class SingletonManager{
    private static Map<String, Object> objMap = new HashMap<>();
    
    private SingletonManager(){}
        
    public static void registerService(String key, Object instance){
    	if(!objMap.containsKey(key)){
    		objMap.put(key, instance);
        }
    }
    
    public static Object getService(String key){
    	return objMap.get(key);	
    }
}
Copy the code

At the beginning of the program, will be a variety of singleton type into a unified management class, when used according to the key to obtain corresponding types of objects, this approach allows us to manage multiple types of singleton, and when use can be obtained through the uniform interface operation, reduce the use of user cost, also hidden from the user the concrete implementation, reduces the coupling.

No matter in which form to realize the singleton pattern, they are the core principles of the constructor private, and by static method to obtain a unique instance, must ensure that thread safety in the process of the acquisition, placed deserialization leads to regenerate the instance objects, choose what kind of way depends on the project itself.

Conclusion:

advantages

  1. Because the singleton pattern has only one instance in memory, it reduces the memory cost, especially when an object needs to be created and destroyed frequently, and the performance is not optimized when creating or destroying, the singleton pattern has obvious advantages
  2. Because the singleton mode only generates one instance, it reduces the performance overhead of the system. When the generation of an object requires more resources, such as reading configuration and generating other dependent objects, it can be solved by directly generating a singleton object when the application is started and then permanently resident it.
  3. The singleton mode can avoid multiple resource usage, for example, a write file operation. Because only one instance exists in memory, simultaneous read and write operations on the same resource file are avoided.
  4. The singleton pattern can set up global access points in the system, optimize and share resource access, for example, you can design a singleton class, responsible for all data table mapping processing.

Disadvantages:

  1. There is generally no excuse for the singleton pattern, it is difficult to extend, and there is little alternative to modifying the code to do so.
  2. If a singleton holds a Context, it is easy to cause memory leaks. In this case, the best Context to pass to the singleton is ApplicationContext

Android source code design pattern analysis and combat

PS: if you think the article is not wrong, you can add the public account to pay attention to the article will be synchronized to the public account.