感謝馮大哥留言,在運用Double-Checked locking的作法時,在Java 5.0之後必須要加上volatile的修飾子在物件的變數宣告上,也就是變成:
- private volatile static Singleton instance;
詳細的過程可以參考這篇文章:Double-checked Locking (DCL) and how to fix it
我覺得寫的還蠻清楚的,另外我也加了相關閱讀在最下方,歡迎大家參考,這幾篇對於Double Check Locking的機制都寫得蠻清楚的,值得一看囉。
==
在Singleton Pattern (單例模式) Part1中的【Lazy Instantization】方法可以說是最基礎的方式,但是在多執行緒的程式當中,這樣的寫法還是有可能會產生不同的實體,也就是這樣的寫法並不是一個thread-safe的方式,我們可以寫一個小程式來檢查看看:
public class SingletonTest extends Thread {
String threadName = "";
public SingletonTest(String name) {
threadName = name;
}
public void run() {
for(int i=0 ; i<10 ; i++) {
try {
Thread.sleep((int)500);
Singleton instance1 = Singleton.getInstance();
System.out.println(threadName + " ==> " + instance1.hashCode());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String [] a) {
Thread t1 = new SingletonTest("thread-1");
Thread t2 = new SingletonTest("thread-2");
t1.start();
t2.start();
}
}
兩個threads可能會產生相同的實體(相同的hashCode)或是不同的實體(但不一定每次都會發生),原因在於兩個threads可能同時呼叫Singleton.getInstance()時,在進行if(instance==null)判斷時,結果都可能會是true,以至於都產生一個新的實體,這樣就會違背Singleton Pattern的宗旨了,所以我們必須進一步的修改這個模式。
修改的想法是,當A、B兩個執行緒同時呼叫Singleton.getInstance()這個方法來取得物件的實體時,必須要先等對方結束之後才呼叫,才不會造成問題。換句話說,getInstance()這個方法在Java中只要加上Synchronized這個修飾子就可以解決了,如此一來,整個getInstance()方法就變成一個同步等待區,可以避免多執行緒造成重複實體的問題。
public class Singleton {
private static Singleton instance;
private Singleton () { }
public static Synchronized Singleton getInstance() {
if(instance==null)
instance = new Singleton();
return instance;
}
}
上面提到,某個執行緒必須等到另外一個執行緒離開之後才能存取getInstance()這個method,當執行緒一多的時候,可以想像的會造成效率的低落。所以又有進一步的改良寫法,那就是採用雙重上鎖(Double-Checked locking)的方式。
雙重上鎖的概念是,一開始先檢查instance這個變數是不是null,如果不是null代表之前已經實體化了,直接回傳就好,如果沒有實體化時,才會進入同步等待區域。實際撰寫上只要將上面的getInstance()方法改寫成以下的形式就可以了:
public class Singleton {
private volatile static Singleton instance;
private Singleton () { }
public static Singleton getInstance() {
if(instance==null) {
Synchronized(Singleton.class) {
instance = new Singleton();
}
}
return instance;
}
}
如此一來就可以解決多執行緒效率的問題囉!歡迎大家一起留言討論:)
【相關閱讀】
Double-checked locking and the Singleton pattern
Double-checked Locking (DCL) and how to fix it
The "Double-Checked Locking is Broken" Declaration
2 意見:
若在 java 用要加上 volatile, 不然還是有 multi-thread 沒有 synchronized 的問題, 請見: http://en.wikipedia.org/wiki/Double-checked_locking
感謝馮大哥的留言,我會在內文加註上去,謝謝:)
張貼留言