说到单例,网上教程和不少人信手拈来: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); } }