The single example pattern is implemented as follows:
Package Com.zzj.pattern.singleton;
public class Singleton {
private static Singleton instance;
Private Singleton () {
} public
static Singleton getinstance () {
if (instance = = null) {
instance = new Sin Gleton ();
}
return instance
}
}
This approach is called deferred initialization, but is invalidated in multi-threaded situations, and uses a synchronous lock to lock the getinstance () method:
public static synchronized Singleton getinstance () {
if (instance = = null) {
instance = new Singleton ();
} return
instance;
}
Synchronization is overhead, we only need to sync while initializing, and the normal code execution path does not need to be synchronized, so there is a double check lock (DCL):
public static Singleton getinstance () {
if (instance = null) {
synchronized (singleton.class) {
if (Insta NCE = = null) {
instance = new Singleton ()
;
}}} return instance;
}
Such a design ensures that only one instance is created, and that only the synchronization lock is added at initialization time, which may seem exquisite, but it raises another problem, which is caused by order reordering.
Instruction reordering is to optimize the instruction and improve the efficiency of program operation. Instruction reordering includes compiler reset and run-time reordering. The JVM specification stipulates that instruction reordering can be performed without affecting the results of a single threaded application. For example instance = new Singleton () can be decomposed into the following pseudo code:
Memory = allocate (); 1: Allocating the object's memory space
ctorinstance (memory); 2: Initialization Object
instance = memory; 3: Set instance point to the memory address just allocated
However, the following reordering follows:
Memory = allocate (); 1: Allocating the object's memory space
instance = memory; 3: Set instance point to the memory address just allocated
//Note that the object has not been initialized at this time.
ctorinstance (memory); 2: Initializing the object
The order of steps 2nd and 3rd will not affect the results of program execution in a single thread case, but it will be different in multi-threaded situations. Thread A executes instance = memory (which is visible to another thread B), at which point thread B executes the outer if (instance = null), finds that the instance is not empty and returns, but gets an instance that is not fully initialized. There is always a risk in use, which is the problem of double check locking.
In view of the shortcomings of the DCL, there is a revised version:
public static Singleton getinstance () {
if (instance = null) {
synchronized (singleton.class) {
Singleton te MP = instance;
if (temp = null) {
synchronized (singleton.class) {
temp = new Singleton ();
}
instance = temp;
}} return instance;
}
The revision attempts to introduce local variables and a second synchronized to solve the problem of order reordering. However, while the Java Language Specification stipulates that code in a synchronized code block must be executed before the object lock is released, no code outside of the synchronized code block can be executed before the object lock is released, meaning that instance = Temp May move to the inner synchronized in the compile or run period, and then raise the same problem as DCL.
After JDK1.5, you can use the volatile variable to prevent the command reordering, allowing the DCL to take effect:
Package Com.zzj.pattern.singleton;
public class Singleton {
private static volatile Singleton instance;
Private Singleton () {
} public
static Singleton getinstance () {
if (instance = = null) {
synchronized (singleton.class) {
if (instance = = null) {
instance = new Singleton ()
;
}}} return instance
}
}
Another semantic of volatile is to ensure the visibility of variable modifications.
The single example pattern is also implemented as follows:
Package Com.zzj.pattern.singleton;
public class Singleton {
private static class Instanceholder {public
static Singleton instance = new Singleton ();
}
Private Singleton () {
} public
static Singleton getinstance () {return
instanceholder.instance;
}
}
This approach is called the deferred initialization placeholder (Holder) class pattern. This model introduces a static internal class (placeholder class) and initializes the instance in the inner class, which guarantees the delay initialization of the singleton instance and guarantees the synchronization. This is a comprehensive model of early initialization (bad-Chinese) and delayed initialization (lazy).
At this point, there are three ways to implement the correct single example pattern:
1. Initialize ahead of time.
Package Com.zzj.pattern.singleton;
public class Singleton {
private static Singleton instance = new Singleton ();
Private Singleton () {
} public
static Singleton getinstance () {return
instance;
}
}
2. Double check lock + volatile.
3. Delayed initialization of the placeholder class pattern.