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