singleton pattern
1. Lazy singleton
1.1 Simplest version
1.2 Synchronized version ['sɪŋkrənaɪzd]
1.3 Double-Check version
1.4 Ultimate version: volatile
2. Hungry singleton
2.1 Hungry singleton Implementation
2.2 Other Implementations
2.2.1 Effective Java 1 - Static Inner Classes
2.2.2 Effective Java 2 - Enums
3. Summary
4. Some Useful Links
The class of a singleton object must ensure that only one instance exists
- Lazy: Refers to the global singleton instance being constructed the first time it is used
- Hungry Chinese style: refers to the construction of a global singleton instance at class loading time
1. Lazy singleton
1.1 Simplest version
//version1
public class Single1{
private static Single1 instance;
public static Single1 getInstance(){
if (instance == null)
instance = new Single1();
return instance;
}
}
Or further, make the constructor private, which prevents it from being called by external classes.
// Version 1.1
public class Single1 {
private static Single1 instance;
private Single1() {}
public static Single1 getInstance() {
if (instance == null) {
instance = new Single1();
}
return instance;
}
}
This way of writing is fine most of the time.
The problem is that when working with multiple threads, if there are multiple threads running at the same time to if (instance == null), and they are all judged to be null, then the two threads will each create an instance - this way, it is not a single instance. example
1.2 synchronized version ['sɪŋkrənaɪzd]
// Version 2
public class Single2 {
private static Single2 instance;
private Single2() {}
public static synchronized Single2 getInstance() {
if (instance == null) {
instance = new Single2();
}
return instance;
}
}
However, this way of writing also has a problem: locking the gitInstance method, although avoiding the multiple instance problems that may occur, will force all threads except T1 to wait, which will actually negatively affect the execution efficiency of the program. influence.
1.3 Double-Check version
The efficiency problem of Version2 code relative to Version1d code is actually to solve the problem of 1% chance, and use a 100% appearance shield. There is an optimization idea, which is to change the 100% appearance of the protective shield to a 1% chance of appearing, so that it only appears in places that may cause multiple instances to appear.
- Is there such a method? Of course there is. The improved code Vsersion3 is as follows:
//version3
public class Single3{
private static Single3 instance;
private Single3(){}
public static Single3 getInstance(){
if (instance == null){
synchronized (Single3.class){
if (instance == null)
instance = new Single3();
}
}
return instance;
}
}
This version of the code looks a bit complicated, notice that there are two if (instance == null) judgments, this is called "Double-Check"
- The first if (instance == null) is actually to solve the efficiency problem in Version2. Only when the instance is null will it enter the synchronized code segment - greatly reducing the chance.
- The second if (instance == null), like Version2, is to prevent the possibility of multiple instances
- this code looks flawless.
……
…… —— Of course, it
just
“looks”, there is still a small probability of problems.
This clarifies why there may be problems here. First, we need to figure out several concepts: atomic operations, instruction rearrangement.
Knowledge point: What is instruction rearrangement?To put it simply, it is the optimization that the computer will do in order to improve the execution efficiency. The execution order of some statements may be adjusted without affecting the final result
- that is, for non-atomic operations, in The atomic operations it splits into may be reordered without affecting the final result.
The following passage is copied directly from Chen Hao's article (Single-instance SINGLETON design pattern in simple terms):
The main reason is the sentence singleton = new Singleton(), which is not an atomic operation. In fact, in the JVM, this sentence probably does the following three things.
1. Allocate memory to the singleton
2. Call the constructor of the Singleton to initialize the member variables to form an instance
3. Point the singleton object to the allocated memory space (the singleton is non-null after this step),
but it exists in the JVM's real-time compiler Optimization of instruction reordering. That is to say, the order of the second and third steps above is not guaranteed, and the final execution order may be 1-2-3 or 1-3-2. If it is the latter, it is preempted by thread 2 before 3 is executed and 2 is not executed. At this time, instance is already non-null (but not initialized), so thread 2 will directly return to instance, then use it, and then logically report an error.
1.4 Final version: volatile
For the problems that may occur in Version3 (of course, this probability is very small, but there are still some~), the solution is: just add the volatile keyword to the instance declaration, Version4 version:
//Version4
public class Single4{
private static volatile Single4 instance;
private Single4(){}
public static Single4 getInstance(){
if (instance == null){
synchronized (Single4.class){
if (instance == null){
instance = new Single4();
}
}
}
return instance;
}
}
volatile
One of the functions of the keyword is to prohibit指令重排
, after the instance is declared asvolatile
, there will be one write operation内存屏蔽
on it, so that the read operation does not need to be called before it is assigned a value.Note: volatile prevents the reordering of instructions in [1-2-3] inside the sentence singleton = new Singleton(), but ensures that before a write operation ([1-2-3]) is completed, no Call the read operation (if (instance == null)).
2. Hungry Chinese Singleton
As mentioned above, Hungry-style singleton refers to the way in which a global singleton instance is constructed during class loading.
2.1 Implementation of Hungry Chinese Singleton
//饿汉式实现
public class SingleB {
private static final SingleB INSTANCE = new SingleB();
private SingleB() {}
public static SingleB getInstance() {
return INSTANCE;
}
}
For a hungry Chinese-style singleton, it's basically perfect.
So its disadvantage is just the disadvantage of the Chinese-style singleton itself - since the initialization of INSTANCE is carried out when the class is loaded, and the loading of the class is done by the ClassLoader, the developer originally initialized it Timing is difficult to grasp precisely:
- It may be a waste of resources due to too early initialization
- If the initialization itself depends on some other data, then it's hard to guarantee that other data will be ready before it is initialized.
Of course, this implementation is also fine if the required singleton takes up few resources and does not depend on other data
2.2 Other implementations
2.2.1 Effective Java 1 - Static Inner Classes
// Effective Java 第一版推荐写法
public class Singleton {
private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
}
}
This is very cleverly written:
- For the inner class SingletonHolder, it is a hungry singleton implementation. When SingletonHolder is initialized, the ClassLoader will ensure synchronization, so that INSTANCE is a true singleton.
- At the same time, since SingletonHolder is an inner class, it is only used in the getInstance() of the Singleton of the outer class, so it is loaded when the getInstance() method is called for the first time.
- It uses ClassLoader to ensure synchronization, while allowing developers to control the timing of class loading. From the inside, it is a hungry singleton, but from the outside, it is indeed a lazy implementation.
2.2.2 Effective Java 2 - Enums
// Effective Java 第二版推荐写法
public enum SingleInstance {
INSTANCE;
public void fun1() {
// do something
}
}
// 使用
SingleInstance.INSTANCE.fun1();
Did you see it? This is an enumeration type...not even a class, minimalist.
Since the process of creating an enum instance is thread-safe, there is no synchronization problem with this way of writing.
The author's evaluation of this method:
This way of writing is functionally similar to the common field method, but it is more concise and provides a serialization mechanism for free, absolutely preventing it from being instantiated, even in the face of complex serialization or reflection attacks. Although this approach has not been widely adopted, single-element enumeration types have become the best way to implement Singleton.
3. Summary
OK, seeing this, do you still think the singleton pattern is the simplest design pattern? Looking back at the singleton implementation in your previous code, do you think it is impeccable?
Maybe we don't have such strict requirements on the implementation of singleton in actual development. For example, if I can guarantee that all getInstances are in a thread, then the first and simplest textbook method is enough. For another example, sometimes, my singleton becomes multiple cases, which may not have much impact on the program...
But if we can understand more details, then if someday the program goes wrong, we can at least One more point to troubleshoot. The sooner you solve the problem, the sooner you can go home and eat... :-D
—— Also, there is no perfect solution, and any method will have a "degree" problem. For example, you think the code is impeccable, but because you are using the JAVA language, there may be some bugs in the ClassLoader... Who runs your code on the JVM, maybe the JVM itself has bugs... Your code runs on the mobile phone On, there may be a problem with the mobile phone system... You live in this universe, maybe the universe itself has some bugs... o(╯□╰)o
So, just try to do the best you can.
- Thank you for spending a lot of time here, I hope you didn't feel wasted.
4. Some useful links
- Explain the single-instance SINGLETON design pattern in simple terms: http://coolshell.cn/articles/265.html
- Java concurrent programming: volatile keyword analysis: http://www.cnblogs.com/dolphin0520/p/3920373.html
- Why doesn't volatile guarantee atomicity while Atomic does? : http://www.cnblogs.com/Mainz/p/3556430.html
- When are classes loaded and initialized? http://www.importnew.com/6579.html