一丶 雙排序的單例模式代碼如下
public class SingletonSofeLanEx {
/**
* 如果要保證是單例
* 構造函數一定是私有的
*/
private SingletonSofeLanEx() {
//DO SOME SING
}
public static SingletonSofeLanEx instace = null;
//靜態的工程方式,同一時刻只有一個線程訪問
public static SingletonSofeLanEx getInstace() {
if (null == instace) { //#2:B線程 看到instace已經不是null啦,就直接return啦,然而此刻 第三步的 初始化還沒進行,使用這個實例肯定會有
synchronized (SingletonSofeLanEx.class){
if (instace == null){
instace = new SingletonSofeLanEx(); //#1:A線程 執行到指令重排的 第二步也就是3,分配內存
}
}
}
return instace;
}
}
二丶 解釋為什么是線程不安全的
我來說一下 SingletonSofeLanEx 在CPU當中的工作流程,總共分為三步
1:memory = allocate() 分配對象內存空間
2:ctorInstance() 初始化對象
3: instace = memory 設置instace分配的內存
jvm和cpu優化會指令重排,上面順序會變成1,3,2
單線程環境下,此順序是沒有問題,2,3 前后沒有依賴性
但是在多線程情況下會有這種情況,具體看#1,#2
三丶 那么如何解決呢,這里的思路是防止重排序,使用 volatile 可防止jvm跟cpu進行重排序指令
最終代碼
public class SingletonSofeLanEx {
/**
* 如果要保證是單例
* 構造函數一定是私有的
*/
private SingletonSofeLanEx() {
//實力的時候運行一些計算
}
//1:memory = allocate() 分配對象內存空間
//2:ctorInstance() 初始化對象
//3: instace = memory 設置instace分配的內存
//jvm和cpu優化重新指令重排,上面會變成1,3,2
public volatile static SingletonSofeLanEx instace = null;
//靜態的工程方式,同一時刻只有一個線程訪問
public static SingletonSofeLanEx getInstace() {
if (null == instace) { //2:B線程 看到instace已經不是null啦,就直接return啦,然后第三步的 初始化還沒進行
synchronized (SingletonSofeLanEx.class){
if (instace == null){
instace = new SingletonSofeLanEx(); //1:A線程 執行到指令重排的 第二步也就是3,分配內存
}
}
}
return instace;
}
}