if (helper == null) { helper = new Helper(); } return helper; } } 顯然,由于整個靜態(tài)工廠方法都是同步化的,因此,不會有兩個線程同時進入這個方法。但是,仔細(xì)審查上面的方法會發(fā)現(xiàn),同步化實際上只在helper變量第一次被賦值之前才有用。在helper變量有了值以后,同步化實際上變成了一個不必要的瓶頸。如果能有一個方法去掉這個小小的額外開銷,不是更加完美了嗎?因此,就有了下面這個設(shè)計“巧妙”的雙重檢查成例。 class Foo { private Helper helper = null; public Helper getHelper() { if (helper == null) { synchronized(this) { if (help == null) { helper = new Helper(); } } } return helper; } } 可以看到,在上面的方法中,同步化僅用來避免多個線程同時初始化這個類,而不是同時調(diào)用這個靜態(tài)工廠方法。如果這是正確的,那么使用這一個成例之后,“懶漢式”單例類就可以擺脫掉同步化瓶頸,達(dá)到一個很妙的境界,如下述代碼: public class LazySingleton { private static LazySingleton m_instance = null; private LazySingleton() { } public static LazySingleton getInstance() { if (m_instance == null) { synchronized(LazySingleton.class) { if (m_instance == null) { m_instance = new LazySingleton(); } } } return m_instance; } } 令人吃驚的是,在C語言里得到普遍應(yīng)用的雙重檢查成例在多數(shù)的Java語言編譯器里面并不成立。上面使用了雙重檢查成例的“懶漢式”單例類,不能工作的基本原因在于,在Java編譯器中,LazySingleton類的初始化與m_instance變量賦值的順序不可預(yù)料。如果一個線程在沒有同步化的條件下讀取m_instance引用,并調(diào)用這個對象的方法的話,可能會發(fā)現(xiàn)對象的初始化過程尚未完成,從而造成崩潰。 一般而言,雙重檢查成例對Java語言來說是不成立的。在一般情況下,使用餓漢式單例模式或者對整個靜態(tài)工廠方法同步化的懶漢式單例模式足以解決在實際設(shè)計工作中遇到的問題。 ——— PS:private volatile static LazySingleton m_instance = null; 改成加volatile的就沒有問題了。 保證對他的操作,不進行指令的重排;這樣的話,就可以保證對他的賦值一定是在初始化之后了。 |
|