In the 23 design modes of GOF, the singleton mode is a relatively simple one. However, sometimes the simpler things become more prone to problems. The following is a detailed discussion of the single-instance design pattern.
The so-called singleton mode, simply put, is to ensure that only one instance of a class exists throughout the application. It is like application in the Java Web, which provides a global variable that is useful for a wide range of purposes, such as saving global data, and achieving global operations.
1, the simplest implementation
First, the simplest implementation that can be thought of is to write the constructor of the class as private, so that other classes cannot instantiate the class. It then returns a static example in the class and returns it to the caller. This allows the caller to use the instance with this reference.
Public class singleton{ privatestaticfinalnew Singleton (); Public Static Singleton getinstance () { return Singleton; } Private Singleton () { }}
As in the above example, an external user who needs to use an instance of Singletonclass can only pass the getinstance () method, and its construction method is private, thus guaranteeing that only one object exists.
2, performance optimization--lazy loaded
The above code is simple, but there is a problem----regardless of whether the class is used, a instance object is created. If this creation is time-consuming, for example to link 10,000 databases (exaggerate ....) ), and this class is not necessarily used, then the creation process is useless, how to do?
To solve this problem, we think of a new solution:
public class Singletonclass { private static Singletonclass instance = ; public static Singletonclass getinstance () { if (instance = null ) {instance = new Singletonclass (); return instance; private Singletonclass () {} }
There are two changes in the code----first, set the instance to null and know if the first time you use the sentence is null to create the object. Because the creation object is not in the declaration, the final modification must be removed.
Let's imagine the process. To use Singletonclass, call the GetInstance () method, the first time to find the instance when null, and then create an object, go back, the second time to use, because this instance thing static, Share an object variable, so the value of instance is not NULL anymore, so the object is no longer created and returned directly.
This process is called lazy loaded, which is the late loading-----until it is loaded by the line.
3. Synchronization
The code above is very clear and simple. However, like the famous saying, "80% error is caused by 20% code optimization". Single-threaded, this code is not a problem, but if it is multithreaded, the trouble comes, let us analyze:
Thread A wants to use Singletonclass to call the GetInstance () method. Because it is the first call, a finds that instance is null, and it begins to create an instance, at which point the CPU takes a time slice switch, thread B starts executing, it uses singletonclass, calls the GetInstance () method, It is also detected that instance is null--, which switches after a has been detected, that is, a does not have time to create the object-so B begins to create it. b After the creation is complete, switch to a to continue execution, because it has been detected, so a will not be detected again, it will directly create the object. In this way, threads A and B each have a Singletonclass object-a single case failure!
The solution is also very simple, that is locking:
Public classsingletonclass{Private StaticSingletonclass instance =NULL; Public synchronized StaticSingletonclass getinstance () {if(Instance = =NULL) {instance=NewSingletonclass (); } returninstance; } PrivateSingletonclass () {}}
As long as getinstance () plus a synchronous lock, one thread must wait for another thread to be created before using this method, which guarantees the uniqueness of the simple interest.
4, but also the performance
The code above is also very clear and simple, however, often simple things are not ideal. There is no doubt that this code has a performance problem----synchronized modified synchronization blocks are several times slower than normal code! If there are many times of getinstance () call, then the performance problem has to be considered?!!
Let us analyze, whether the whole method must be locked, or tightly one of the words lock is enough? Why do we have to lock it up? The reason for analyzing the situation of lazy loaded is that the detection of NULL is separated from the operation that created the object, resulting in the uniqueness of only the sync lock to simple interest.
If these two operations can be atomic, then the simple interest is guaranteed. So we started to modify the code:
Public classsingletonclass{Private StaticSingletonclass instance =NULL; Public StaticSingletonclass getinstance () {synchronized(Singletonclass.class){ if(Instance = =NULL) {instance=NewSingletonclass (); } } returninstance; } PrivateSingletonclass () {}}
The first step is to remove the getinstance () and then load the sync lock onto the IF statement. However, such modifications do not make any difference: The performance problem persists because each time a call to getinstance () is bound to go through the line synchronization. If............ Shall we judge in advance whether it is null to synchronize?
Public classsingletonclass{Private StaticSingletonclass instance =NULL; Public StaticSingletonclass getinstance () {if(Instance = =NULL){ synchronized(Singletonclass.class){ if(Instance = =NULL) {instance=NewSingletonclass (); } } } returninstance; } PrivateSingletonclass () {}}
Do you have any questions? First determine if instance is NULL, if NULL is going to synchronize, and if not NULL, the instance object is returned directly.
This is the double---checked----locking design implements simple interest mode. So far, everything is perfect. We implemented a singleton pattern in a very clever way.
5. Check from source
Let's start by saying the compiler principle. The so-called compilation, that is, the source code "translated" into the target code----mostly refers to the process of machine code----. For Java, its target code is not local machine code, but virtual machine code. A very important part of the compiler principle is compiler optimizations. The so-called compiler optimization means that, without altering the original semantics, the program runs faster by adjusting the order of the statements. This process becomes reorder.
You know, the JVM is just a standard, not an implementation. There is no provision in the JVM for compiler optimizations, which means that JVM implementations are free to perform compiler optimizations.
Let's consider what steps are required to create a variable. One is to apply a piece of memory, call the constructor to initialize the operation, and the other is to assign a pointer to this block of memory. Which of the two operations who are in front of who in the post? The JVM specification is not specified. So there is a case where the JVM first creates a piece of memory, then points the pointer to the memory and finally calls the constructor method to initialize it.
Let's consider a situation where thread a begins to create an instance of Singletonclass, at which point thread B calls the getinstance () method and first determines whether instance is null. According to the memory model we mentioned above, A has pointed instance to that block of memory, but has not yet called the constructor method, so B detects that instance is not NULL, and then returns the instance directly-the problem arises, although instance is not NULL, But it's not built, like a house has given you a key, but you can't live in it because it hasn't been cleaned up. At this point, if B is using this instance before a instance construct is completed, the program will get an error!
So, we think of the following code:
Public classSingletonclass {Private StaticSingletonclass instance =NULL; Public StaticSingletonclass getinstance () {if(Instance = =NULL) {Singletonclass sc; synchronized(Singletonclass.class) {SC=instance; if(sc = =NULL) { synchronized(Singletonclass.class) { if(sc = =NULL) {SC=NewSingletonclass (); }} instance=SC; } } } returninstance; } PrivateSingletonclass () {}}
We create a temporary variable inside the first synchronization block, and then use this temporary variable to create the object, and at last put the instance pointer to the memory space of the temporary variable. Writing this code is based on the idea that synchronized will act as a code mask, and that the code inside the synchronization block is not associated with the external code. Therefore, the operation of the temporary variable SC within the external synchronization block does not affect the instance, so the outer class is INSTANCE=SC before the instance is detected, and the result instance remains null. However, this idea is entirely
ErrorOf The release of the synchronization block is guaranteed to be done before this-that is, the synchronization block-but there is no guarantee that the operation after the synchronization block cannot be reversed by the compiler optimizations until the end of the synchronization block. Therefore, the compiler can completely put instance=sc; this sentence is moved to the internal synchronization block inside the execution. In this way, the program is wrong again!
6. SolutionSo much, isn't there a single example that can be implemented in Java? actually otherwise After JDK 5, Java uses a new memory model. The volatile keyword has explicit semantics--before JDK1.5, volatile is a keyword, but it does not explicitly specify its purpose--a volatile write variable cannot be adjusted with the previous read-write code, the read variable cannot be adjusted with the reading and writing code! So, as long as we simply add the volatile keyword to the instance.
Public classSingletonclass {Private volatile StaticSingletonclass instance =NULL; Public StaticSingletonclass getinstance () {if(Instance = =NULL) { synchronized(Singletonclass.class) { if(Instance = =NULL) {instance=NewSingletonclass (); } } } returninstance; } PrivateSingletonclass () {}}
However, this is just the Java solution after JDK1.5, what about the previous version? In fact, there is another solution that is not affected by the Java version:
Public classSingletonclass {Private Static classSingletonclassinstance {Private Static FinalSingletonclass instance =NewSingletonclass (); } Public StaticSingletonclass getinstance () {returnsingletonclassinstance.instance; } PrivateSingletonclass () {}}
In this version of the singleton pattern Implementation code, we used the static inner class of java. This technique is explicitly explained by the JVM, so there is no ambiguity. In this code, because Singletonclass does not have a static property, it is not initialized. Until getinstance () is called, the Singletonclassinstance class is loaded first, and the class has a static Singletonclass instance, so you need to call Singletonclass's construction method. Then getinstance () will return the instance of this inner class to the user. Since this instance is static, it is not constructed more than once. Because Singletonclassinstance is a private static inner class, it is not known by other classes, and static semantics also requires that no more than one instance exists. Also, the JSL specification defines that the construction of a class must be atomic, non-concurrent, and therefore does not require the addition of synchronous blocks. Similarly, because this construct is concurrent, getinstance () does not need to be synchronized. Now that we have a complete understanding of the singleton pattern in the Java language, we present two solutions. The individual is biased towards the second, and Effiective Java is also recommended in this way.
Singleton mode for Java design mode