如果不加volatile關(guān)鍵字的單例模式寫法如下:
public static Singleton getSingleton() {
if (instance ==null) {//Single Checked
synchronized (Singleton.class) {
if (instance ==null) {//Double Checked
instance =new Singleton();
}
}
}
return instance;
}
這段代碼看起來很完美孙技,很可惜,它是有問題屹蚊。主要在于instance = new Singleton()這句末捣,這并非是一個原子操作,事實上在 JVM 中這句話大概做了下面 3 件事情蔚出。
1弟翘、給 instance 分配內(nèi)存
2、調(diào)用 Singleton 的構(gòu)造函數(shù)來初始化成員變量
3骄酗、將instance對象指向分配的內(nèi)存空間(執(zhí)行完這步 instance 就為非 null 了)
但是在 JVM 的即時編譯器中存在指令重排序的優(yōu)化稀余。也就是說上面的第二步和第三步的順序是不能保證的,最終的執(zhí)行順序可能是 1-2-3 也可能是 1-3-2趋翻。如果是后者睛琳,則在 3 執(zhí)行完畢、2 未執(zhí)行之前踏烙,被線程二搶占了师骗,這時 instance 已經(jīng)是非 null 了(但卻沒有初始化),所以線程二會直接返回 instance,然后使用,然后順理成章地報錯肯腕。
所以需要加上volatile關(guān)鍵字,禁止指令重排序愿待,也就是將對象初始化之后浩螺,再將instance指向分配對象的內(nèi)存空間。
public class Singleton {
private volatile static Singleton instance;//聲明成 volatile
private Singleton (){}
public static Singleton getSingleton() {
if (instance ==null) {
synchronized (Singleton.class) {
if (instance ==null) {
instance =new Singleton();
}
}
}
return instance;
}
}