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 implementationFirst, 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, and then provide a static instance in the class and return it to the consumer. This allows the user to use this reference to an instance of the class. PublicclassSingletonclass {
PrivateStatic FinalSingletonclass instance =NewSingletonclass ();
PublicStaticSingletonclass getinstance () {
returnInstance
}
PrivateSingletonclass () {
}
As in the example above, if an external user needs to use an instance of Singletonclass, only through the getinstance () method, and its construction method is private, this ensures that only one object exists.
2. Performance optimization--lazy loadedThe code above is simple, but there is a problem--whether or not the class is used, a instance object is created. If this creation process is time consuming, such as the need to connect 10,000 databases (exaggerated ...:-)), and this class is not necessarily used, then this creation process is useless. What do we do? To solve this problem, we think of a new solution: PublicclassSingletonclass {
PrivateStaticSingletonclass instance =NULL;
PublicStaticSingletonclass getinstance () {
if(Instance = =NULL) {
Instance =NewSingletonclass ();
}
returnInstance
}
PrivateSingletonclass () {
}
There are two changes in the code-first, to initialize the instance to null until the first time it is used to create the object by judging whether it is null. Because the creation process 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 I found out that instance was null, and then I created a new object and returned it, and the second time I used it, because the instance was static, it was not null, so no more objects were made and returned directly. This process becomes the lazy loaded, which is Changaca, which is loaded only when it is used.
3. SynchronizationThe code above is very clear and simple. However, like the famous saying, "80% of errors are caused by the 20% code optimization." Single-threaded, this code is not a problem, but if it is multi-threading, the trouble comes. Let's analyze: Thread A wants to use Singletonclass and 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: PublicclassSingletonclass {
PrivateStaticSingletonclass instance =NULL;
PublicsynchronizedStaticSingletonclass getinstance () {
if(Instance = =NULL) {
Instance =NewSingletonclass ();
}
returnInstance
}
PrivateSingletonclass () {
}
To 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 Singleton.
4. Again performanceThe above code is very clear and simple, however, simple things are often not ideal. There is no doubt that this code has a performance problem--synchronized modified synchronization blocks are several times slower than normal code snippets! If there are many calls to getinstance (), then the performance problem has to be considered! Let us analyze whether the whole method must be locked, or just one of the words to lock is enough? Why do we have to lock it up? Analyze the reasons for the situation where lazy loaded is present. The reason is that the operation to detect null is separated from the operation that created the object. If these two operations can be done atomically, then the singleton is guaranteed. So we started to modify the code: PublicclassSingletonclass {
PrivateStaticSingletonclass instance =NULL;
Public StaticSingletonclass getinstance () {
synchronized(Singletonclass.class) {
if(Instance = =NULL) {
Instance =NewSingletonclass ();
}
}
returnInstance
}
PrivateSingletonclass () {
}
First, the synchronization of the getinstance () is removed, and then the sync Lock is loaded on the IF statement. However, such modifications do not make any difference: because the time to invoke getinstance () is bound to synchronize, the performance problem is still there. If...... What if we decide in advance if it is null and then synchronize? PublicclassSingletonclass {
Private StaticSingletonclass instance =NULL;
PublicStaticSingletonclass getinstance () {
if(Instance = =NULL) {
synchronized(Singletonclass.class) {
if(Instance = =NULL) {
Instance =NewSingletonclass ();
}
}
}
returnInstance
}
PrivateSingletonclass () {
}
Do you have any questions? First, the instance is not NULL, if NULL, the lock is initialized, and if not NULL, the instance is returned directly. This is the double-checked locking design to implement a singleton mode. So far, everything is perfect. We implemented a singleton pattern in a very clever way.
5. Check from sourceLet's start by saying the compiler principle. Compiling is the process of "translating" the source code into the target code-most of it refers to the 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: PublicclassSingletonclass {
PrivateStaticSingletonclass instance =NULL;
PublicStaticSingletonclass 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. SolutionWith so much to say, is there no way to implement this in Java in a single case? 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. PublicclassSingletonclass {
PrivatevolatileStaticSingletonclass instance =NULL;
PublicStaticSingletonclass 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: PublicclassSingletonclass {
PrivateStaticclasssingletonclassinstance {
PrivateStaticFinalSingletonclass instance =NewSingletonclass ();
}
PublicStaticSingletonclass 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.
Drill down into Java singleton mode