7种单例模式

来源:互联网 发布:网络游戏运营数据挖掘 编辑:程序博客网 时间:2024/06/11 16:25
有奖征资源,博文分享有内涵      社区问答:芈峮iOS测试指南     专访阿里陶辉     2014 CSDN博文大赛      10月微软MVP申请

【JAVA学习】单例模式的七种写法

分类: 【JAVA学习】 163人阅读 评论(0)收藏 举报
线程安全单例懒汉饿汉多线程

尊重版权:http://cantellow.iteye.com/blog/838473

 

第一种(懒汉,线程不安全):

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static Singleton instance;  
  3.     private Singleton (){}  
  4.   
  5.     public static Singleton getInstance() {  
  6.     if (instance == null) {  
  7.         instance = new Singleton();  
  8.     }  
  9.     return instance;  
  10.     }  
  11. }  
 

 这种写法lazy loading很明显,但是致命的是在多线程不能正常工作。

第二种(懒汉,线程安全):

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static Singleton instance;  
  3.     private Singleton (){}  
  4.     public static synchronized Singleton getInstance() {  
  5.     if (instance == null) {  
  6.         instance = new Singleton();  
  7.     }  
  8.     return instance;  
  9.     }  
  10. }  
 

 这种写法能够在多线程中很好的工作,而且看起来它也具备很好的lazy loading,但是,遗憾的是,效率很低,99%情况下不需要同步。

第三种(饿汉):

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static Singleton instance = new Singleton();  
  3.     private Singleton (){}  
  4.     public static Singleton getInstance() {  
  5.     return instance;  
  6.     }  
  7. }  
 

 这种方式基于classloder机制避免了多线程的同步问题,不过,instance在类装载时就实例化,虽然导致类装载的原因有很多种,在单例模式中大多数都是调用getInstance方法, 但是也不能确定有其他的方式(或者其他的静态方法)导致类装载,这时候初始化instance显然没有达到lazy loading的效果。

第四种(饿汉,变种):

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private Singleton instance = null;  
  3.     static {  
  4.     instance = new Singleton();  
  5.     }  
  6.     private Singleton (){}  
  7.     public static Singleton getInstance() {  
  8.     return this.instance;  
  9.     }  
  10. }  
 

 表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化instance。

第五种(静态内部类):

 

Java代码  收藏代码
  1. public class Singleton {  
  2.     private static class SingletonHolder {  
  3.     private static final Singleton INSTANCE = new Singleton();  
  4.     }  
  5.     private Singleton (){}  
  6.     public static final Singleton getInstance() {  
  7.     return SingletonHolder.INSTANCE;  
  8.     }  
  9. }  
 

这种方式同样利用了classloder的机制来保证初始化instance时只有一个线程,它跟第三种和第四种方式不同的是(很细微的差别):第三种和第四种方式是只要Singleton类被装载了,那么instance就会被实例化(没有达到lazy loading效果),而这种方式是Singleton类被装载了,instance不一定被初始化。因为SingletonHolder类没有被主动使用,只有显示通过调用getInstance方法时,才会显示装载SingletonHolder类,从而实例化instance。想象一下,如果实例化instance很消耗资源,我想让他延迟加载,另外一方面,我不希望在Singleton类加载时就实例化,因为我不能确保Singleton类还可能在其他的地方被主动使用从而被加载,那么这个时候实例化instance显然是不合适的。这个时候,这种方式相比第三和第四种方式就显得很合理。

第六种(枚举):

 

Java代码  收藏代码
  1. public enum Singleton {  
  2.     INSTANCE;  
  3.     public void whateverMethod() {  
  4.     }  
  5. }  

 

 这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象,可谓是很坚强的壁垒啊,不过,个人认为由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。

第七种(双重校验锁):

Java代码  收藏代码
  1. public class Singleton {  
  2.     private volatile static Singleton singleton;  
  3.     private Singleton (){}  
  4.     public static Singleton getSingleton() {  
  5.     if (singleton == null) {  
  6.         synchronized (Singleton.class) {  
  7.         if (singleton == null) {  
  8.             singleton = new Singleton();  
  9.         }  
  10.         }  
  11.     }  
  12.     return singleton;  
  13.     }  
  14. }  
 

 这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看:http://www.ibm.com/developerworks/cn/java/j-dcl.html

在JDK1.5之后,双重检查锁定才能够正常达到单例效果。

 

总结

有两个问题需要注意:

1.如果单例由不同的类装载器装入,那便有可能存在多个单例类的实例。假定不是远端存取,例如一些servlet容器对每个servlet使用完全不同的类装载器,这样的话如果有两个servlet访问一个单例类,它们就都会有各自的实例。

2.如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和复原。不管怎样,如果你序列化一个单例类的对象,接下来复原多个那个对象,那你就会有多个单例类的实例。

对第一个问题修复的办法是:

 

Java代码  收藏代码
  1. private static Class getClass(String classname)      
  2.                                          throws ClassNotFoundException {     
  3.       ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     
  4.       
  5.       if(classLoader == null)     
  6.          classLoader = Singleton.class.getClassLoader();     
  7.       
  8.       return (classLoader.loadClass(classname));     
  9.    }     
  10. }  

 对第二个问题修复的办法是:

 

Java代码  收藏代码
  1. public class Singleton implements java.io.Serializable {     
  2.    public static Singleton INSTANCE = new Singleton();     
  3.       
  4.    protected Singleton() {     
  5.         
  6.    }     
  7.    private Object readResolve() {     
  8.             return INSTANCE;     
  9.       }    
  10. }   
 

对我来说,我比较喜欢第三种和第五种方式,简单易懂,而且在JVM层实现了线程安全(如果不是多个类加载器环境),一般的情况下,我会使用第三种方式,只有在要明确实现lazy loading效果时才会使用第五种方式,另外,如果涉及到反序列化创建对象时我会试着使用枚举的方式来实现单例,不过,我一直会保证我的程序是线程安全的,而且我永远不会使用第一种和第二种方式,如果有其他特殊的需求,我可能会使用第七种方式,毕竟,JDK1.5已经没有双重检查锁定的问题了。

========================================================================

 superheizai同学总结的很到位:

 

不过一般来说,第一种不算单例,第四种和第三种就是一种,如果算的话,第五种也可以分开写了。所以说,一般单例都是五种写法。懒汉,恶汉,双重校验锁,枚举和静态内部类。

我很高兴有这样的读者,一起共勉。

更多1
0
0
主题推荐
java线程安全 多线程 对象 工作
猜你在找
oracle 采用游标循环插入数据的存储过程
C++之文件IO操作流
深入学习:Windows下Git入门教程(下)
Eclipse入门
C++ Primer 学习笔记_97_用于大型程序的工具 --多重继承与虚继承[续2]
面试对象你真的明白吗?
【C语言学习】C语言中的函数
Xcode 使用笔记
数学之美读后感
Java中的double类型数据存储探析
查看评论

  暂无评论

发表评论
  • 用 户 名:
  • u011127637
  • 评论内容:
  • 插入代码
      
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
快速回复
核心技术类目
全部主题JavaVPNAndroidiOSERPIE10EclipseCRMJavaScriptUbuntuNFCWAPjQuery数据库BIHTML5SpringApacheHadoop.NETAPIHTMLSDKIISFedoraXMLLBSUnitySplashtopUMLcomponentsWindows MobileRailsQEMUKDECassandraCloudStackFTCcoremailOPhoneCouchBase云计算iOS6RackspaceWeb AppSpringSideMaemoCompuware大数据aptechPerlTornadoRubyHibernateThinkPHPSparkHBasePureSolrAngularCloud FoundryRedisScalaDjangoBootstrap
    个人资料

    o玉蜀黍o
    • 访问:3743次
    • 积分:277分
    • 排名:千里之外
    • 原创:24篇
    • 转载:7篇
    • 译文:0篇
    • 评论:6条
    文章分类
  • 【UI设计】(5)
  • 【JAVA学习】(20)
  • 【前端开发】(5)
  • 【其他】(1)
    文章存档
  • 2014年07月(4)
  • 2014年06月(8)
  • 2014年05月(1)
  • 2014年04月(13)
  • 2014年03月(5)
    阅读排行
  • 【UI设计】扁平化设计之流行色值(388)
  • 【UI设计】优秀前段学习资源汇总(转)(292)
  • 【前段开发】行内元素和块级元素总结(HTML CSS)(273)
  • 【前段开发】10步掌握CSS定位: position static relative absolute float(272)
  • 【其他】笔记本的辐射有多大?(211)
  • 【前端开发】25种提高网页加载速度的方法和技巧(176)
  • 【JAVA学习】单例模式的七种写法(163)
  • 【前端开发】提高网站加载速度(162)
  • 【JAVA学习】Linux(ubuntu)下成功配置jdk1.7.0_51(159)
  • 【JAVA学习】struts2 中 Actionsupport 的作用(154)
    评论排行
  • 【其他】笔记本的辐射有多大?(3)
  • 【JAVA学习】Java中迭代器Iterator遍历的两种用法(2)
  • 【UI设计】优秀前段学习资源汇总(转)(1)
  • 【UI设计】 TIME(0)
  • 【JAVA学习】JAVA中int、String的类型转换(0)
  • 【JAVA学习】读取文件getClassLoader().getResourceAsStream("xxx")(0)
  • 【JAVA学习】序列化与反序列化(0)
  • 【JAVA学习】拦截器与过滤器的区别(0)
  • 【JAVA学习】struts2 中 Actionsupport 的作用(0)
  • 【JAVA学习】struts2的action中使用session的方法(0)
    推荐文章
    最新评论
  • 【其他】笔记本的辐射有多大?

    o玉蜀黍o:@ch656409110:平时多注意点健康

  • 【其他】笔记本的辐射有多大?

    o玉蜀黍o:@ch656409110:女孩也挺好的,只要别不能生就好。

  • 【其他】笔记本的辐射有多大?

    夜空中苦逼的程序员:男程序员大多生女孩。。太多太多了。。哎

  • 【UI设计】优秀前段学习资源汇总(转)

    janey1222:太牛了。。。

  • 【JAVA学习】Java中迭代器Iterator遍历的两种用法

    o玉蜀黍o:@u013630150:嘿嘿,失误失误,谢谢指正。

  • 【JAVA学习】Java中迭代器Iterator遍历的两种用法

    拿媳妇换糖糖:你确定那三个方法是构造方法?


公司简介|招贤纳士|广告服务|银行汇款帐号|联系方式|版权声明|法律顾问|问题报告|合作伙伴|论坛反馈
网站客服杂志客服 微博客服 webmaster@csdn.net 400-600-2320
京 ICP 证 070598 号
北京创新乐知信息技术有限公司 版权所有
江苏乐知网络技术有限公司 提供商务支持
Copyright © 1999-2014, CSDN.NET, All Rights Reserved GongshangLogo
更多相关资源: 类java
0 0
原创粉丝点击