一开始可能会这样写:
public class Singleton {
private static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
这里的 getInstance 方法是线程不安全的:
这时线程1 和线程2 就会返回不同的值。
为了解决解决它,我们可以用 sychronized 关键字修饰 getInstance 方法或在 getInstance 方法内添加 sychronized 块。
public sychronized static Singleton getInstance(){}
或
public static Singleton getInstance() {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
return instance;
}
但是每次都同步会显得开销过大,其实只有当 instance 为 null 时才需要进行同步,所以我们在同步前进行判断,修改如下:
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
然而这样还是会出现问题,问题出在 instance = new Singleton() 这条语句上,这条语句执行时会有如下三个步骤:
- 创建一个对象 t,为它分配内存;
- 对 t 进行初始化;
- 将 t 赋值给 instance(instance != null);
编译器在执行时可能会改变指令的执行顺序使吞吐量最大化,即指令乱排序,所以上面的 2 和 3 的顺序不能保证,所以一个线程在 instance != null 时拿到的 instance 可能是一个没有初始化的 instance。
为了解决乱排序的问题,需要将 instance 用 volatile 修饰,volatile 有两层含义:
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。
- 禁止进行指令重排序。
通过禁止指令重排序,上面的问题就可以解决,完整代码如下:
public class Singleton {
private volatile static Singleton instance;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}