`
popwang
  • 浏览: 58766 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

7种单例模式

 
阅读更多

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

 

Java代码  收藏代码
  1. public   class  Singleton {  
  2.     private   static  Singleton instance;  
  3.   
  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.   
  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.   
  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.   
  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.   
  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.   
  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 同学总结的很到位:

 

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

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

鉴于很多人说我没有写私有构造方法,我这里声明一下,不是我忘了写,是我故意嫌麻烦省略掉了,我以为大家都懂得

分享到:
评论

相关推荐

    单例模式的多种实现.docx

    3. 单例模式的7种实现方式 5 3.1饿汉式(使用静态常量) 5 3.2饿汉式(使用静态代码块) 7 3.3懒汉式(线程不安全) 8 3.4懒汉式(线程安全,使用同步方法) 9 3.5双重检查实现单例模式 10 3.6使用静态内部类实现...

    Java设计模式之单例模式的七种写法

    此文档为Tom老师的公开课的单例的7种写法的一个文档,充分分析单例模式,值得对设计模式有研究的童鞋下下来好好看看

    单例模式的七种写法

    单例模式的七种写法: 1.第一种(懒汉,线程不安全) 2.第二种(懒汉,线程安全) 3.第三种(饿汉) .....

    Java版的7种单例模式写法示例

    主要给大家介绍了关于Java版的7种单例模式写法,文中通过示例代码介绍的非常详细,对大家学习或者使用Java具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧

    关于单例模式的知识要点

    关于单例模式的知识要点: 1、某个类只能有一个实例 2、它必须自行创建这个实例 3、必须自行向这个系统提供这个实例

    java单例模式

    java单例模式开发的7中写法,网上搜索的,可以看看

    单例模式.zip(c#设计模式)

    本压缩包含有两个demo,分别对应<设计模式>第2版,刘伟这本书,单例模式这一章课后习题6、7,如果问题请留言一起探讨,谢谢!

    工厂模式与单例模式

    NULL 博文链接:https://zhaohong.iteye.com/blog/483396

    xfhy#Android-Notes#7.单例模式的双重检查锁为什么必须加volatile1

    这种写法可以保证线程安全.两个if都是不能去掉的.如果去掉第一个if: 那么所有的线程都会到这里来先获取锁,然后判断singleton是否为空.所有线程都会串行

    设计模式—— 七 :单例模式

    单例模式的实现饿汉式懒汉式线程安全的懒汉式登记式单例模式的优缺点单例模式的优点单例模式的缺点 单例模式是23个模式中比较简单的模式,应用也非常广泛 什么是单例模式? 单例模式的定义: Ensure a class ...

    这可能是最全的单例模式了

    设计模式之——单例模式单例的几种实现1. 懒汉单例模式2. synchronized 修饰的懒汉单例模式3. 双重检查锁定的单例模式4. 静态内部类实现单例模式5. 饿汉实现单例模式6. 饿汉变种实现单例模式7. 枚举实现单例模式...

    单例模式简介以及C++版本的实现

     单例模式是一种设计模式,它的具体实现和各种语言特性有关,这里主要介绍在C++上面的实现,测试平台为Win7 64位,VS2010开发环境。  根据参考博文中的例子,在此先列举一下各种实现策略,以下均以CSingleton为...

    php实现的mongoDB单例模式操作类

    最后我就封装了一个单例模式的数据库类 使用单例模式是为了避免生成多个实例,浪费资源 下面是封装的代码 class Mongo_db { private static $cli; /** * 不允许初始化 */ private function __construct() { ...

    C#中的五种单例模式(SIngleton)

    1. 写法一(只适用于单线程环境) public sealed class Singleton1 { private Singleton1(){} private static Singleton1 instance = null; public static Singleton1 Instance { get ...

    23种设计模式demo

    创建型模式(5种):工厂方法模式,抽象工厂模式,单例模式,建造者模式,原型模式。 结构型模式(7种):适配器模式,装饰器模式,代理模式,外观模式,桥接模式,组合模式,享元模式。 行为型模式(11种):策略...

    精品源码 C# 27种设计模式源代码

    2 单例模式 2 门面模式 2 模板方法模式 3 策略模式 3 工厂方法模式 3 组合模式 4 抽象工厂模式 4 代理模式 4 命令模式 5 迭代器模式 5 适配器模式 5 原型模式 6 简单工厂模式 6 解释器模式 6 享元模式 7 桥接模式 7 ...

    23种设计模式 -设计模式图解.7z

    创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。 结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。 行为型模式,共十一种:...

    看了这篇文章,你能和面试官畅谈单例模式

    其实单例模式在C#或者.NET里面更好理解,像win7的任务管理器,在系统中只能创建一个。有些理解了嘛? 单例模式只能有一个实例,实例化其实就是new的过程,是不可能阻止他人不去用new的。所以我们完全可以直接就把这...

Global site tag (gtag.js) - Google Analytics