In the world of design patterns, the singleton model may be the simplest. Although simple, it still needs to go through a little twists and turns if you want to thoroughly understand it.
Next, let's take a look:
In actual development, we only need one object, such as the thread pool, cache, dialog box, log object, and task manager. These objects can only have one instance. Multiple instances may cause many problems.
You may say why we don't need Java global variables, which is convenient. Yes, but this method has some disadvantages. If we assign an object to a global variable, we must create an object at the beginning of the program. Once this object is very resource-consuming, the program has never been used in execution, so this is a waste.
In Singleton mode, to ensure that an object can only be instantiated once, let's look at a prototype code:
public class Singleton {private static Singleton uniqueInstance;private Singleton(){}public static Singleton getInstance(){if(uniqueInstance==null){uniqueInstance = new Singleton();}return uniqueInstance;}}
Here, we use a static variable to record the unique instance of the singleton class and set the constructor to private. This constructor can be called only within the singleton class. Then we use the getinstance () method to instantiate the object and return this instance. If the uniqueinstance is empty, it indicates that no instance has been created. If it is not empty, it indicates that an object has been created before, and the Return Statement is skipped directly.
But if you think about it, you will find that the above Code has some problems. Where is the problem?
If we want to apply multithreading, if two or more threads want to execute the above Code, multiple instance objects may be generated, that is, there is no synchronization mechanism, if both threads run uniqueinstance = NULL, two objects will be generated, and the result is incorrect.
You may say that you add the synchronized keyword to the method. Yes, the following code is available:
public class Singleton {private static Singleton uniqueInstance;private Singleton(){}public static synchronized Singleton getInstance(){if(uniqueInstance==null){uniqueInstance = new Singleton();}return uniqueInstance;}}
In this way, we add the synchronized keyword to the getinstance () method, so that each thread waits for other threads to leave the method before entering this method. That is to say, no two threads can enter this method at the same time.
But what is the problem? Think about it. It is very simple:Synchronization reduces performance
That is to say, we only need to synchronize this method for the first time. Once the uniqueinstance variable is set, we no longer need to synchronize this method, because only the first unqueinstance = NULL, every time this method is called, synchronization is redundant and even cumbersome.
Therefore, sometimes it is necessary to flexibly respond to the situation.
If the performance of getinstance () is not very important to the application, do nothing, but you must know that, synchronizing a method may result in a 100-fold reduction in program execution efficiency, so many times we have to reconsider.
Of course, if we are sometimes eager to create an instance without delaying instantiation, we will create a singleton in the static initiator, as shown in the following code:
public class Singleton {private static Singleton uniqueInstance=new Singleton();private Singleton(){}public static synchronized Singleton getInstance(){return uniqueInstance;}}
This code is thread-safe.
Next let's look at the final version of the singleton mode:
Double check locking, reduce the use of synchronization in getinstance:
Double-checked locking is used to check whether the instance has been created. If the instance has not been created, it is synchronized, this is exactly what we want:
public class Singleton {private volatile static Singleton uniqueInstance;private Singleton(){}public static Singleton getInstance(){if(uniqueInstance==null){synchronized (Singleton.class){if(uniqueInstance==null){uniqueInstance = new Singleton();}}}return uniqueInstance;}}
The volatile keyword is used to notify other threads of variable update operations, to ensure that new values can be synchronized to the primary memory immediately, and to refresh from the primary memory immediately before each use. When the variable is declared as volatile, the compiler and runtime will notice that the variable is shared.
Here, the volatile keyword ensures that when the uniqueinstance variable is initialized to a singleton instance, multiple threads can correctly process the uniqueinstance variable.
If performance is the focus of your relationship, this practice can help you greatly reduce the time consumption of getinstance.
Design Pattern-Singleton pattern