[Design mode] Singleton Mode
1. Introduction to singleton Mode
The single-profit model is one of the most widely used models, and may be the only design model that many junior engineers will use. When this mode is applied, the class of the singleton object must ensure that only one instance exists. In many cases, the whole system only needs to have one global object, which is helpful for us to coordinate the overall behavior of the system. For example, there should be only one ImageLoader instance in an application. This ImageLoader contains thread pools, cache systems, and network requests, which consume resources. Therefore, there is no reason for him to construct multiple instances.
This kind of situation where the object cannot be constructed freely is the use scenario of the single-profit model.
2. Singleton mode definition
Make sure that a class has only one instance, and instantiate it and provide the instance to the entire system.
3. Use Cases of Singleton Mode
To ensure that a class has only one object, avoid generating multiple objects that consume too much resources, or a certain type of object should only have one and only one.
For example, creating an object consumes too many resources. If you want to access IO, database, and other resources, you must consider using the singleton mode.
4. UML class diagram of Singleton Mode
Role introduction:
(1) Client --- high-level Client;
(2) Singleton-Singleton class
The Singleton mode has the following key points:
(1) The constructor is not open to the outside, generally Private;
(2) return the singleton class object through a static method or enumeration
(3) ensure that there is only one and only one single-instance Class Object, especially in a multi-threaded environment;
(4) ensure that the singleton class object does not re-build the object during deserialization.
By privatize the constructor of the singleton class, client code cannot manually construct the objects of the singleton class in the form of new. The Singleton class exposes a common static method. The client needs to call this static method to obtain the unique object of the singleton class. In the process of this singleton object, it is necessary to ensure thread security, and constructing a singleton class in a multi-threaded environment also has one and only one object, which is also difficult to implement in singleton mode.
5. Simple examples of Singleton Mode
Public class Staff {public void work () {// work }} public class VP extends Staff {@ Overrride public void work {// manage the following managers} public static class CEO extends Staff {private static final CEO mCeo = new CEO; // constructor private CEO () {}// public static function, exposes the interface for obtaining the singleton object. public static CEO getCEO () {return mCeo ;} @ Override public void work () {// manage VP} public class Company {private List
AllStaffs = new ArrayList
(); Public void addStaff (Staff per) {allStaffs. add (per);} public void showAllStaffs () {for (Staff per: allStaffs) {System. out. println ("Obj" + per. toString () ;}} public class Test {public static void main (String [] args) {Company cp = new Company (); Staff ceo1 = CEO. getCEO (); Staff cerium = CEO. getCEO (); cp. addStaff (ceo1); cp. addStaff (Cerium); Staff p0 = new VP (); Staff VPNs = new VP (); Staff staff1 = new Staff (); Staff staff2 = new Staff (); staff staff3 = new Staff (); cp. addStaff (P0); cp. addStaff (VP2.); cp. addStaff (staff1); cp. addStaff (staff2); cp. addStaff (staff3); cp. showAllStaffs ();}}
The output result shows that the CEO class cannot construct objects in the form of new, but only the CEO can. the getCEO () function is used to obtain the CEO object. The CEO object is a static object and has been initialized during its lifecycle. This ensures the uniqueness of the CEO object. From the output results, we found that the CEO objects output by the CEO are the same twice, while VP, Staff, and other types are different, the core of this implementation is to privatize the CEO's constructor, so that external programs cannot construct CEO objects through constructor, while the CEO class returns a static object through a static method.
6. Other implementation methods of Singleton mode 6.1 lazy Mode
The lazy mode declares a static object and initializes the object when the user calls getInstance for the first time. The aforementioned hunger mode (CEO class) is initialized when the static object is declared.
Lazy Singleton mode is implemented as follows:
public class Singleton{ private static Singleton instance; private Singleton(){} public static synchronized Sigleton getInstance(){ if(instance == null){ instance = new Singleton(); } return instance; }}
GetInstance () adds the synchronized keyword, that is, getInstance () is a synchronization method. This is the method mentioned above to ensure the uniqueness of the singleton object in the case of multithreading.
In this case, even if the instance has been initialized (the instance will be initialized during the first call), every call to getInstance will be synchronized, which will consume unnecessary resources, this is also the problem of the lazy Singleton model.
In conclusion, the advantage of the lazy Singleton mode is that a single instance is instantiated only when used, saving resources to a certain extent.
The disadvantage is that the instance needs to be instantiated in time during the first loading, and the response is slow. The biggest problem is that the synchronization is performed every time getInstance is called, causing unnecessary synchronization overhead.
This mode is generally not recommended.
6.2 Double CheckLock (DCL) Implementation Singleton
The advantages of the DCL mode in singleton mode are that the singleton mode can be initialized only when required, and the thread security can be ensured. After the singleton object is initialized, getInstance is called to avoid synchronization locks.
The Code is as follows:
public class Singleton{ private static Singleton sInstance = null; private Singleton(){ } public void doSometing(){ System.out.println("do sth."); } public static Singleton getInstance(){ if(mInstahce == null){ synchronized(Singleton.class){ if(mInstance == null){ sInstance = new Singleton(); } } } return sInstance; }}
The highlights of this program are naturally on the getInstance method. We can see that the getInstance method has made two judgments on the instance: the first judgment is mainly to avoid unnecessary synchronization, the second layer is used to create an instance in the case of null. What does this mean?
First, we need to know the sInstance = new Singleton () Statement, which has roughly done three things:
(1) allocate memory to Singleton instances;
(2) Call the Singleton () constructor to initialize the member field;
(3) direct the sInstance object to the allocated memory space (in this case, sInstance is not null ).
The second and third orders above cannot be guaranteed in the JMM long Cache before JDK1.5 and the sequence in which registers are written back to the primary memory. That is to say, the execution sequence may be 1-2-3 or 1-3-2. If it is the latter, and before 3 is finished and 2 is not executed, it is switched to thread B. At this time, sInstance has already executed the third point in thread, the sInstance is no longer empty. Therefore, thread B directly removes the sInstance and then encounters an error. This is the problem of DCL failure and is difficult to trace.
After JDK1.5, SUN adjusted JMM to embody the volatile keyword. Therefore, you only need to change the sInstance definition to private volatile static Singleton sInstance = null to ensure that the sInstance object is read from the main memory every time. Then you can use DCL to complete the Singleton mode, of course, using volatile also affects performance more or less.
DCL: resource utilization is high. The first time getInstance is executed, the single-instance object will be instantiated, which is highly efficient.
Disadvantages: The first loading was slow, and sometimes failed due to the memory model of java. It also had some defects in High-concurrency environments, although the probability of occurrence was very small. The DCL mode is the most widely used Singleton implementation method. It can instantiate Singleton objects only when necessary and ensure the uniqueness of Singleton objects in most scenarios. Unless the code is complex in the concurrent scenario.
6.3 static internal class Singleton Mode
Although DCL solves problems such as resource consumption, redundant synchronization, and thread security to a certain extent, it still has a Time Limitation Problem under certain circumstances, this problem is called lock failure of double check, which is discussed in java concurrent programming practice.
We recommend that you use the following code to replace
Public class Singleton {private Singleton () {} public static Singleton getInstance () {return SingletonHolder. sInstance;} // static internal class private static class SingletonHolder {private static final Singleton sInstance = new Singleton ();}}
SInstance is not initialized when the Singleton class is loaded for the first time. sInstance is initialized only when the Singleton getInstance method is called for the first time. Therefore, the first call to the getInstance method will cause the virtual machine to load the SingletonHolder class. This method not only ensures thread security, but also ensures the uniqueness of the singleton object. colleagues also delay Singleton instantiation, therefore, we recommend that you use the singleton mode.
6.4 enumeration Singleton
Enumeration is the simplest Singleton implementation method.
public enum SingleEnum{ INSTANCE; public void doSomething{ System.out.println("do sth."); }}
Simple Writing is the biggest advantage of enumeration. enumeration is the same as common classes in Java. It not only has fields, but also has its own methods. More importantly, the creation of enumeration instances is thread-safe by default, and it is a singleton under any circumstances.
6.5 use containers for Singleton Mode
public class SingletonManager{ private static Map
objMap = new HashMap
(); private Singleton(){} public static void registerService(String key,Object instance){ if (!objMap.containKey(key)) { objMap.put(key,instance); } } public static ObjectgetService(String key){ return objMap.get(key); }}
In this form, multiple Singleton types can be injected into a unified management class, and objects of the corresponding type can be obtained based on the key during use. This method allows us to manage multiple types of Singleton instances, and can be acquired through a unified interface during use. This reduces users' usage costs and hides specific implementations from users, reduced coupling.
All in all, regardless of the form of Singleton mode, his core principle is to privatize the constructor and obtain a unique instance through the static method, in this process, we must ensure thread security and prevent deserialization from re-generating instance objects.