Simple understanding and use of Java singleton mode
Java singleton pattern
1. The realization of Hungry Chinese singleton is as follows:
//饿汉式实现
public class SingleB {
private static final SingleB INSTANCE = new SingleB();
private SingleB() {
}
public static SingleB getInstance() {
return INSTANCE;
}
}
2. The ultimate lazy version: volatile
// Version 4
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;
}
}
The main reason lies in the sentence singleton = new Singleton(), which is not an atomic operation. In fact, this sentence probably does the following three things in the JVM:
- 1. Allocate memory to singleton
- 2. Call the constructor of Singleton to initialize member variables and form an instance
- 3. Point the singleton object to the allocated memory space (singleton is non-null after executing this step)
But there are optimizations for instruction reordering in the JVM's just-in-time compiler. That is to say, the order of the second and third steps above cannot be guaranteed, and the final execution order may 1-2-3
or be 1-3-2
:
- If it is the latter, it will be preempted by thread 2 before 3 is executed and 2 is not executed. At this time
instance 已经是非 null了(但却没有初始化)
, thread 2 will directly return to instance, then use it, and then report an error logically.
Explain a little more, that is to say, because there is one 『instance已经不为null但是仍没有完成初始化』的中间状态
, and at this time, if other threads happen to run to the first layer if (instance == null), the instance read here is no longer null, so directly If the instance in this intermediate state is used, problems will arise.
- The key here is - thread T1 to instance
写操作没有完成
, thread T2就执行了读操作
.
The role of the volatile keyword
One function of the volatile keyword is 禁止指令重排
that after the instance is declared volatile, there will be one write operation on it 内存屏障
, so that there is no need to call the read operation before its assignment is completed.
- Note:
volatile
What prevents the reordering of instructionssingleton = newSingleton()
in this sentence , but ensures that it will not be called until one is complete .内部[1-2-3]
写操作([1-2-3])
读操作(if(instance == null))
3. Effective Java 1 - static inner class
// 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 way of writing is very clever:
- For the inner class
SingletonHolder
, it is a hungry singleton implementation, whichSingletonHolder
willClassLoader
ensure , makingINSTANCE
it a true singleton. - At the same time, since
SingletonHolder
it is an internal class, it is only used in ()Singleton
of the external class, so the timing when it is loaded is when the getInstance() method is called for the first time.getInstance
- It uses
ClassLoader
to ensure synchronization, while allowing developers to control the timing of class loading. From the inside, it looks like a hungry singleton, but from the outside, it is indeed a lazy implementation.
4.5.2 Effective Java 2 - Enumeration
// Effective Java 第二版推荐写法
public enum SingleInstance {
INSTANCE;
public void fun1() {
// do something
}
}
// 使用
SingleInstance.INSTANCE.fun1();