经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » Java相关 » 设计模式 » 查看文章
挑战常规--这样写单例是错的!
来源:cnblogs  作者:我想嘿嘿  时间:2018/10/18 9:00:17  对本文有异议

说到单例,网上教程和很多人信手拈来:

  1. public class Single
  2. {
  3. private volatile static Single instance;
  4.  
  5. private Single()
  6. {
  7. System.out.println("创建单例");
  8. }
  9. public static Single getInstance()
  10. {
  11. if (instance == null)
  12. {
  13. synchronized (Single.class)
  14. {
  15. if (instance == null)
  16. {
  17. instance = new Single();
  18. }
  19. }
  20. }
  21. return instance;
  22. }
  23. }

 自信满满,称之为懒汉加载模式言之节省内存,用时才会自动创建。在我看来,这种写法完全是错误的,愚蠢至极,这不是脱裤子放屁,这是脱完裤子再提取裤子再放屁。

正确写法就是最简单的:

  1. public class Single
  2. {
  3. private static Single instance=new Single();
  4.  
  5. private Single()
  6. {
  7. System.out.println("创建单例");
  8. }
  9. public static Single getInstance()
  10. {
  11. return instance;
  12. }
  13. }

 

下面驳斥所谓的省内存。

  1. public class SingleTest
  2. {
  3. public static void main(String[] args) throws Exception
  4. {
  5. System.out.println("启动线程");
  6. System.in.read();
  7. }
  8.  
  9. }

 首先,不用单例时,难道别的写法就会加载单例?没有引用使用单例类时,当然都不会加载单例。

看图,并不会加载创建单例,控制台也不会输出创建单例。

其次,既然预定要使用单例,那么都会加载创建一次。

  1. public class SingleTest
  2. {
  3. public static void main(String[] args) throws Exception
  4. {
  5. System.out.println("启动线程");
  6. Single.getInstance();
  7. System.in.read();
  8. }
  9.  
  10. }

 看图,无论哪种模式单例都会被引用加载

 

是不是用synchronized创建单例就没有用处了呢?

并不是synchronized是用于解决多线程访问问题带参数的单例创建才应该使用懒汉模式

因为并不能预期,什么时候参数被传入。简单模式下并不清楚传入什么参数,或参数对象未初始化。

 

  1. public class Single
  2. {
  3. private volatile static Single instance ;
  4. public Single(Context context)
  5. {
  6. System.out.println("创建单例");
  7. }
  8. public static Single getInstance(Context context)
  9. {
  10. if (instance == null)
  11. {
  12. synchronized (Single.class)
  13. {
  14. if (instance == null)
  15. {
  16. instance = new Single(context);
  17. }
  18. }
  19. }
  20. return instance;
  21. }
  22. }

 

 为什么说这个是为了解决多线程访问呢,先看如果不加锁会发生什么

  1. public class Single
  2. {
  3. private static Single instance ;
  4. public Single(Context context)
  5. {
  6. System.out.println("创建单例");
  7. }
  8. public static Single getInstance(Context context)
  9. {
  10. if (instance == null)
  11. {
  12. instance = new Single(context);
  13. }
  14. return instance;
  15. }
  16. }
  1. public class SingleTest
  2. {
  3.  
  4. public static void main(String[] args) throws Exception
  5. {
  6. ExecutorService pool = Executors.newCachedThreadPool();
  7. ArrayList<Callable<Void>> runners=new ArrayList<>();
  8. for(int i=0;i<10;i++)
  9. {
  10. runners.add(()->{
  11. Single.getInstance(new Context());
  12. return null;
  13. });
  14. }
  15. System.out.println("启动线程");
  16. pool.invokeAll(runners);
  17. pool.shutdown();
  18. System.in.read();
  19. }
  20.  
  21. }

 不加锁情况,结果看图

 

加锁情况,结果看图

 

总结,无参构造单例无需复杂的加入synchronized,而未确定的传参单例需要加synchronized保证多线程访问安全。

思考,如果传入的参数确定,怎么写才最优呢?下面类似写法是否合理:

 

  1. public class Single
  2. {
  3. private static Single instance =new Single(Context.instance);
  4. public Single(Context context )
  5. {
  6. System.out.println("创建单例");
  7. }
  8. public static Single getInstance( )
  9. {
  10. return instance;
  11. }
  12. }

 

 

  1. @WebListener
  2. public class MyServletContextListener implements ServletContextListener {
  3. public void contextDestroyed(ServletContextEvent sce) {
  4. }
  5. public void contextInitialized(ServletContextEvent sce) {
  6. Single.getInstance(sce);
  7. }
  8. }

 

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号