說到單例,網上教程和不少人信手拈來:java
public class Single { private volatile static Single instance; private Single() { System.out.println("建立單例"); } public static Single getInstance() { if (instance == null) { synchronized (Single.class) { if (instance == null) { instance = new Single(); } } } return instance; } }
自信滿滿,稱之爲懶漢加載模式,言之節省內存,用時纔會自動建立。在我看來,這種寫法徹底是錯誤的,愚蠢至極,這不是脫褲子放屁,這是脫完褲子再提取褲子再放屁。安全
正確寫法就是最簡單的:多線程
public class Single { private static Single instance=new Single(); private Single() { System.out.println("建立單例"); } public static Single getInstance() { return instance; } }
下面駁斥所謂的省內存。spa
public class SingleTest { public static void main(String[] args) throws Exception { System.out.println("啓動線程"); System.in.read(); } }
首先,不用單例時,難作別的寫法就會加載單例?沒有引用使用單例類時,固然都不會加載單例。線程
看圖,並不會加載建立單例,控制檯也不會輸出建立單例。3d
其次,既然預約要使用單例,那麼都會加載建立一次。code
public class SingleTest { public static void main(String[] args) throws Exception { System.out.println("啓動線程"); Single.getInstance(); System.in.read(); } }
看圖,不管哪一種模式單例都會被引用加載對象
是否是用synchronized建立單例就沒有用處了呢?blog
並非,synchronized是用於解決多線程訪問問題。帶參數的單例建立才應該使用懶漢模式教程
由於並不能預期,何時參數被傳入。簡單模式下並不清楚傳入什麼參數,或參數對象未初始化。
public class Single { private volatile static Single instance ; public Single(Context context) { System.out.println("建立單例"); } public static Single getInstance(Context context) { if (instance == null) { synchronized (Single.class) { if (instance == null) { instance = new Single(context); } } } return instance; } }
爲何說這個是爲了解決多線程訪問呢,先看若是不加鎖會發生什麼
public class Single { private static Single instance ; public Single(Context context) { System.out.println("建立單例"); } public static Single getInstance(Context context) { if (instance == null) { instance = new Single(context); } return instance; } }
public class SingleTest { public static void main(String[] args) throws Exception { ExecutorService pool = Executors.newCachedThreadPool(); ArrayList<Callable<Void>> runners=new ArrayList<>(); for(int i=0;i<10;i++) { runners.add(()->{ Single.getInstance(new Context()); return null; }); } System.out.println("啓動線程"); pool.invokeAll(runners); pool.shutdown(); System.in.read(); } }
不加鎖狀況,結果看圖
加鎖狀況,結果看圖
總結,無參構造單例無需複雜的加入synchronized,而未肯定的傳參單例須要加synchronized保證多線程訪問安全。
思考,若是傳入的參數肯定,怎麼寫才最優呢?下面相似寫法是否合理:
public class Single { private static Single instance =new Single(Context.instance); public Single(Context context ) { System.out.println("建立單例"); } public static Single getInstance( ) { return instance; } }
@WebListener public class MyServletContextListener implements ServletContextListener { public void contextDestroyed(ServletContextEvent sce) { } public void contextInitialized(ServletContextEvent sce) { Single.getInstance(sce); } }