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

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

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

这样的写法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. if(classLoader == null)
  5. classLoader = Singleton.class.getClassLoader();
  6. return (classLoader.loadClass(classname));
  7. }
  8. }

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

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

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

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

superheizai同学总结的非常到位:

只是一般来说,第一种不算单例,第四种和第三种就是一种。假设算的话,第五种也能够分开写了。所以说,一般单例都是五种写法。

懒汉,恶汉。双重校验锁,枚举和静态内部类。

我非常高兴有这种读者。一起共勉。

【JAVA学习】单例模式的七种写法的更多相关文章

  1. Java:单例模式的七种写法

    第一种(懒汉,线程不安全): 1 public class Singleton { 2 private static Singleton instance; 3 private Singleton ( ...

  2. Java:单例模式的七种写法(转载)

    第一种(懒汉,线程不安全): package Singleton; /** * @echo 2013-10-10 懒汉 线程不安全 */ public class Singleton1 { priva ...

  3. Java:单例模式的七种写法[转]

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  4. Java:单例模式的七种写法<转>

    第一种(懒汉,线程不安全):  1 public class Singleton {   2     private static Singleton instance;   3     privat ...

  5. 温故而知新(java实现)单例模式的七种写法

    第一种(懒汉,线程不安全): Java代码 public class Singleton { private static Singleton instance; private Singleton ...

  6. Java设计模式之单例模式(七种写法)

    Java设计模式之单例模式(七种写法) 第一种,懒汉式,lazy初始化,线程不安全,多线程中无法工作: public class Singleton { private static Singleto ...

  7. Java 单例模式的七种写法

    Java 单例模式的七种写法 第一种(懒汉,线程不安全) public class Singleton { private static Singleton instance; private Sin ...

  8. Android设计模式之单例模式的七种写法

    一 单例模式介绍及它的使用场景 单例模式是应用最广的模式,也是我最先知道的一种设计模式.在深入了解单例模式之前.每当遇到如:getInstance()这样的创建实例的代码时,我都会把它当做一种单例模式 ...

  9. KandQ:单例模式的七种写法及其相关问题解析

    设计模式中的单例模式可以有7种写法,这7种写法有各自的优点和缺点: 代码示例(java)及其分析如下: 一.懒汉式 public class Singleton { private static Si ...

随机推荐

  1. 完整的Android手机短信验证源码

    短信验证功能我分两个模块来说,短信验证码的后台和代码实现短信验证码的功能. 一.短信验证码的后台      1.注册Mob账号:http://www.mob.com/#/login 2.注册成功之后, ...

  2. iOS开发之自定义导航栏返回按钮右滑返回手势失效的解决

    我相信针对每一个iOS开发者来说~除了根视图控制器外~所有的界面通过导航栏push过去的界面都是可以通过右滑来返回上一个界面~其实~在很多应用和APP中~用户已经习惯了这个功能~然而~作为开发者的我们 ...

  3. ViewPager,模仿慕课网

    源码:http://pan.baidu.com/s/1DhM14 使用fragment实现的:http://pan.baidu.com/s/1mgzWlM4 SecondActivity.java p ...

  4. A*算法的C语言实现

    #include ”stdio.h“ #include “conio.h” #include ”assert.h“ #include “stdlib.h” #define MAPMAXSIZE 100 ...

  5. 转:git教程 ~~非常好的入门教程

    --------------------------------------- notes: cdmkdir dirnametouchls > filenamelsecho "this ...

  6. Mojo 自动发布接口

    get '/api/auto_publish/publish' => sub{ my $c = shift; my $env = $c->param('env'); my $app = $ ...

  7. word2vec 中的数学原理具体解释(三)背景知识

      word2vec 是 Google 于 2013 年开源推出的一个用于获取 word vector 的工具包,它简单.高效,因此引起了非常多人的关注.因为 word2vec 的作者 Tomas M ...

  8. Android的BUG(二) - SurfaceTexture中的野指针

    当初遇到这个bug,是不定期的低概率出现,最后找到一个比较容易重现的步骤: 启动系统 然后进google +  新建一个帐号(注意是新建一个帐号)  没几步就重启了 这个BUG,一开始追踪也是无头绪的 ...

  9. Sql语句中使用参数化的Top

    在sql中使用参数化的Top,Top后面的参数要用括号括起来. 例如: select top (@ts) ID, [Type], Title, Content, LinkMan, Tel, Check ...

  10. c++空指针调用类成员函数

    最近在看C++动态绑定问题时(理解静态绑定时)发现的问题:能用空指针调用类的成员函数(gcc,vs2013下都可以). 例子: class animal { public: void sleep(){ ...