1. 程式人生 > 其它 >雙重校驗鎖實現單例模式

雙重校驗鎖實現單例模式

package day1;
/** 特點:懶漢模式,只有使用時,才進行建立
* 首次呼叫getInstance()才使用synchronized加鎖,後續使用時無需加鎖
* 該程式碼存在效率問題,因為即使已經產生了單例之後,之後呼叫了getInstance()方法之後仍然還會加鎖解鎖,
* 加鎖和解鎖會影響系統的效能
* ====================>
* 於是有了雙重校驗鎖機制
*
*/
public final class Singleton {
private static Singleton instance = null;
private Singleton() {} // 私有的構造方法,只能類內呼叫
public static Singleton getInstance() {
synchronized (Singleton.class) { // 避免多執行緒併發訪問
if (instance == null) {
instance = new Singleton();
}
return instance;
}
}
}

final class Singleton_1 {
private Singleton_1() {
}

private static Singleton_1 instance = null;

public static Singleton_1 getInstance() {
if (instance == null) {
// 首次訪問會同步,之後的不會進入synchronized方法
synchronized (Singleton_1.class) {
if (instance == null) {
instance = new Singleton_1();
}
}
}
return instance;
}
}
/**
* 但是該方法仍然存在問題
* * if (instance == null) 在synchronized外部,instance靜態變數不是有序的,synchronized無法禁止指令重排
* * instance = new Singleton_1();
* * 位元組碼包含4部分:
* * 1. new 新建一個例項
* * 2. 複製這個例項的引用
* * 3. 通過該引用呼叫構造方法
* * 4. 引用用來進行賦值操作putstatic
* *
* * JIT即時編譯器進行指令重排,將4排到3的前面,那麼t1還未完全將構造方法執行完畢,如果在構造方法中要執行很多初始化操作
* * 那麼t2拿到的instance類變數是非空的,但是一個未初始化完畢的單例,那麼就會出現問題
* * 解決辦法,就是對instance使用volatile修飾
*/

// 完整版
final class Singleton_2 {
private Singleton_2() {}
private volatile static Singleton_2 instance = null;
public static Singleton_2 getInstance() {
// 讀屏障,寫屏障之後的不能指令重排
if (instance == null) {
// 首次訪問會同步,之後的不會進入synchronized方法
synchronized (Singleton_2.class) {
if (instance == null) {
instance = new Singleton_2();
// 寫屏障,寫屏障之前的不能指令重排
}
}
}
return instance;
}
}