思想:html
相比於餓漢模式,懶漢模式實際中的應用更多,由於在系統中,「被用到時再初始化」是更佳的解決方案。多線程
設計思想與餓漢模式相似,一樣是持有一個自身的引用,只是將 new 的動做延遲到 getinstance() 方法中執行。post
public final class LazySingleton { private static LazySingleton instance; private LazySingleton() { if (instance != null) { throw new IllegalStateException(); } } public static synchronized LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
對於 LazySingleton,這是個頗有趣的問題,雖然咱們在私有構造器中增長了 instance==null 的判斷,可是因爲延遲加載的緣由,使得它沒法完美地規避反射的入侵。性能
這涉及到了反射入侵和 getInstance() 方法調用順序的問題。優化
若是在調用 getInstance() 方法以前進行反射入侵,那麼就會打破單例,反之,能夠保證單例。url
public class LazySingletonTest { @Test public void testReflectSuccess() throws Exception { Constructor<?> constructor = LazySingleton1.class.getDeclaredConstructor(); constructor.setAccessible(true); LazySingleton1 singleton1 = (LazySingleton1) constructor.newInstance(); LazySingleton1 singleton2 = LazySingleton1.getInstance(); Assert.assertNotSame(singleton1, singleton2); } @Test public void testReflectFailure() throws Exception { LazySingleton1 singleton1 = LazySingleton1.getInstance(); Constructor<?> constructor = LazySingleton1.class.getDeclaredConstructor(); constructor.setAccessible(true); try { LazySingleton1 singleton2 = (LazySingleton1) constructor.newInstance(); Assert.fail(); } catch (Exception e) { // Do nothing, test pass } } }
由於是延遲加載,考慮到多線程狀況,須要對方法同步。spa
可使用 synchronized 代碼塊 + Double-check Locking + volatile 關鍵字,對 LazySingleton 進行深一步優化,詳情見:第003彈:懶漢型單例模式的演變線程
優點:延遲加載。設計
劣勢:不能徹底屏蔽反射入侵,並且代碼較爲繁瑣。code