http://blog.csdn.net/nsw911439370/article/details/50456231
Turn https://biezhi.me/article/how-to-correctly-write-singleton-pattern.html
The single example pattern is the easiest to understand and the easiest way to write code in design mode. But there are a lot of pits, so also often as a face test questions. This paper mainly deals with several kinds of single cases, and analyzes their advantages and disadvantages. A lot of it is a cliché, but if you don't know how to create a single thread-safe case and don't know what a double check lock is, that article might help you. Lazy type, thread unsafe
When asked to implement a single case pattern, many people's first reaction was to write the following code, including what the textbook taught us.
public class Singleton {
private static Singleton instance;
Private Singleton () {} public
static Singleton getinstance () {
if (instance = = null) {
instance = new Singleto n ();
}
return instance
}
}
This code is straightforward and uses lazy load mode, but there are fatal problems. When multiple threads call getinstance () in parallel, multiple instances are created. In other words, it does not work under multithreading. Lazy, thread-safe
The easiest way to solve the above problem is to set the entire getinstance () method to sync (synchronized).
public static synchronized Singleton getinstance () {
if (instance = = null) {
instance = new Singleton ();
} return
instance;
}
Although it is thread-safe and solves multiple-instance problems, it is not efficient. Because only one thread can call the GetInstance () method at any time. However, the synchronization operation needs to be required only on the first call, that is, when a singleton instance object is first created. This leads to a double check lock. Double check Lock
Double-check lock mode (double checked locking pattern) is a method of using synchronized blocks to lock. The programmer calls it a double check lock because there will be two checks instance = NULL, once outside the sync block and once in the sync block. Why do I have to check again in the sync block? Because there may be more than one thread going into the sync block together, multiple instances will be generated if no two tests are performed within the synchronization block.
public static Singleton Getsingleton () {
if (instance = null) { //single Checked
synchronized ( Singleton.class) {
if (instance = = null) { //double Checked
instance = new Singleton ()
;
}}} return
instance;
}
This code looks perfect, unfortunately, it is problematic. The main reason is that instance = new Singleton (), this is not an atomic operation, in fact in the JVM this sentence probably did the following 3 things. Allocating memory to the instance call Singleton constructor to initialize the member variable to point the instance object to the allocated memory space (instance is not NULL after this step)
However, there is an optimization of instruction reordering in the JVM's Just-in-time compiler. In other words, the order of the second and third steps above is not guaranteed, and the final order of execution may be 1-2-3 or 1-3-2. If it is the latter, then the 3 execution, 2 is not executed before the thread two preemption, then instance has been non-null (but not initialized), so thread two will return directly to instance, and then use, and then logically to the error.
We just need to declare the instance variable as volatile.
public class Singleton {
Private volatile static Singleton instance;//declared as volatile
private Singleton () {}
PU Blic static Singleton Getsingleton () {
if (instance = = null) {
synchronized (singleton.class) {
if (instance = = null) {
instance = new Singleton ();
}} return instance
}
}
Some people think that the reason for using volatile is visibility, which is to ensure that the thread does not hold a copy of the instance locally, each time it is read in the main memory. But it's not right, actually. The main reason for using volatile is another feature: banning command reordering optimizations. That is, there is a memory barrier (on the generated assembler code) behind the assignment operation of the volatile variable, and the read operation is not reordered before the memory barrier. For example, the above example, the fetch operation must be done after 1-2-3 or after the 1-3-2, there is no execution to 1-3 and then fetch the value of the case. From the point of view of "first occurrence principle", it is that the write operation for a volatile variable occurs first in the face of the variable reading ("Back" is the order of time).
However, it is also problematic to pay special attention to the use of volatile dual-check locks in previous versions of Java 5. The reason is that the previous Java 5 JMM (Java memory model) is flawed, the immediate declaration of variables into volatile can not completely avoid reordering, mainly volatile variables before and after the code still has a reordering problem. This volatile screen reordering problem is fixed in Java 5, so you can safely use volatile after that.
I believe you will not like this complex and hidden problem, of course, we have a better way to implement a single example of thread safety. A hungry man-static final field
This approach is very simple, because instances of a singleton are declared static and final variables, initialized when the class is first loaded into memory, so creating the instance itself is thread-safe.
The public class singleton{
//class is loaded with the initialization of the
private static final Singleton instance = new Singleton ();
Private Singleton () {} public
static Singleton getinstance () {return
instance;
}
}
If the writing is perfect, there is no need to nag so many double check locks. The disadvantage is that it is not a lazy load mode (lazy initialization), which is initialized at the start of the load class, even if the client does not invoke the GetInstance () method. A hungry man creation is not possible in some scenarios: for example, Singleton instance creation is a dependency parameter or a configuration file, and a method setting parameter must be invoked before getinstance (), so that this single example cannot be used. Static internal class static nested class
I prefer to use the static inner class method, which is also recommended in the "effective Java".
public class Singleton {
private static class Singletonholder {
private static final Singleton INSTANCE = new Sing Leton ();
}
Private Singleton () {} public
static final Singleton getinstance () {return
singletonholder.instance;
}
}
This method of writing still uses the JVM's own mechanism to ensure thread-safety issues; Because Singletonholder is private, there is no way to access it except for getinstance (), so it is lazy; there is no synchronization when reading instances, no performance flaws, and no reliance on JDK version. Enum enum
It's too easy to write a single example with enumerations. This is also its biggest advantage. The following code is the usual way to declare an enumeration instance.
public enum easysingleton{
INSTANCE;
}
We can access the instance through Easysingleton.instance, which is much simpler than calling the GetInstance () method. Creating enumerations By default is thread-safe, so you don't have to worry about double checked locking, and you can prevent deserialization from causing a new object to be recreated. But it's still rare to see someone write this, probably because you're not familiar with it. Summarize
In general, the single case pattern has five kinds of wording: lazy, a hungry man, double check lock, static internal class, enumeration. Above all is the realization of thread safety, the first method given at the beginning of the article is not correct.
As far as I am concerned, it would be nice to use the A hungry man directly in general, if the explicit requirement to lazy load (lazy initialization) will tend to use static internal classes, if it involves deserialization to create an object, try to use the enumeration method to implement the single example.