`
竹临仙
  • 浏览: 38145 次
  • 性别: Icon_minigender_1
  • 来自: 重庆
社区版块
存档分类
最新评论

单例模式 转转转

阅读更多
单例模式的七种写法
文章分类:Java编程

转载请注明出处:http://cantellow.iteye.com/blog/838473



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


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. } 

public class Singleton {
    private static Singleton instance;

    public static Singleton getInstance() {
if (instance == null) {
    instance = new Singleton();
}
return instance;
    }
}

这种写法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. } 

public class Singleton {
    private static Singleton instance;

    public static synchronized Singleton getInstance() {
if (instance == null) {
    instance = new Singleton();
}
return instance;
    }
}

这种写法能够在多线程中很好的工作,而且看起来它也具备很好的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. } 

public class Singleton {
    private static Singleton instance = new Singleton();

    public static Singleton getInstance() {
return instance;
    }
}

这种方式基于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. } 

public class Singleton {
    private Singleton instance = null;
    static {
instance = new Singleton();
    }

    public static Singleton getInstance() {
return this.instance;
    }
}



表面上看起来差别挺大,其实更第三种方式差不多,都是在类初始化即实例化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. } 

public class Singleton {
    private static class SingletonHolder {
private static final Singleton INSTANCE = new Singleton();
    }

    public static final Singleton getInstance() {
return SingletonHolder.INSTANCE;
    }
}



这种方式同样利用了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. } 

public enum Singleton {
    INSTANCE;
    public void whateverMethod() {
    }
}



这种方式是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. } 

public class Singleton {
    private volatile static Singleton singleton;

    public static Singleton getSingleton() {
if (singleton == null) {
    synchronized (Singleton.class) {
if (singleton == null) {
    singleton = new Singleton();
}
    }
}
return singleton;
    }
}



这个是第二种方式的升级版,俗称双重检查锁定,详细介绍请查看: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. } 

private static Class getClass(String classname)   
                                         throws ClassNotFoundException {  
      ClassLoader classLoader = Thread.currentThread().getContextClassLoader();  
   
      if(classLoader == null)  
         classLoader = Singleton.class.getClassLoader();  
   
      return (classLoader.loadClass(classname));  
   }  
}

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


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. }  

public class Singleton implements java.io.Serializable {  
   public static Singleton INSTANCE = new Singleton();  
   
   protected Singleton() {  
     
   }  
   private Object readResolve() {  
            return INSTANCE;  
      } 
}



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

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

superheizai同学总结的很到位:



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

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

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

8

重构实践之一

    * 13:02
    * 浏览 (8948)
    * 评论 (39)
    * 分类: Java
    * 收藏Spinner
    * 相关推荐

评论
39 楼 xiaohui886688 昨天   引用
受益匪浅,。。。
 
38 楼 清晨阳光 2010-12-17   引用
如果用第三种,建议声明为final的,因为我们没有理由不将其声明为final的。另外,关于双重锁定,建议你看看EhCache的源代码里的CacheManager类,new了新的实力后,应该在synchronized块内 return,其余一致,包括将实例声明为原子的。
附EhCache项目CacheManager部分源代码:
Java代码

   1. public static CacheManager create() throws CacheException { 
   2.     if (singleton != null) { 
   3.         return singleton; 
   4.     } 
   5.     synchronized (CacheManager.class) { 
   6.         if (singleton == null) { 
   7.             LOG.debug("Creating new CacheManager with default config"); 
   8.             singleton = new CacheManager(); 
   9.         } else { 
  10.             LOG.debug("Attempting to create an existing singleton. Existing singleton returned."); 
  11.         } 
  12.         return singleton; // 这里是在synchronized块内返回的,而你的例子不是 
  13.     } 
  14. } 

public static CacheManager create() throws CacheException {
    if (singleton != null) {
        return singleton;
    }
    synchronized (CacheManager.class) {
        if (singleton == null) {
            LOG.debug("Creating new CacheManager with default config");
            singleton = new CacheManager();
        } else {
            LOG.debug("Attempting to create an existing singleton. Existing singleton returned.");
        }
        return singleton; // 这里是在synchronized块内返回的,而你的例子不是
    }
}

37 楼 yunchow 2010-12-17   引用
搞这么多没意思 , 直接饿汉才是王道
36 楼 kyfxbl 2010-12-17   引用
建议你深入学习一下“茴”的4种写法
35 楼 guyinglong 2010-12-16   引用
public class Singleton { 
    /**
     * 类级的内部类,也就是静态的成员式内部类,该内部类的实例与外部类的实例
     * 没有绑定关系,而且只有被调用到才会装载,从而实现了延迟加载
     */
    private static class SingletonHolder{ 
        /**
         * 静态初始化器,由JVM来保证线程安全
         */
        private static Singleton instance = new Singleton(); 
    } 
    /**
     * 私有化构造方法
     */
    private Singleton(){ 
    } 
    public static  Singleton getInstance(){ 
        return SingletonHolder.instance; 
    } 
}

高人写的
34 楼 i2534 2010-12-15   引用
cantellow 写道
yanical 写道
请问博主在什么资料上看到在5.0后双重检查模式是有效的?

不知道在JE上看到的算不算,忘记是哪篇文章了。

建议还是懒汉模式吧,dcl还是比较麻烦而且容易出错的.不差这点内存吧.
33 楼 cantellow 2010-12-15   引用
yanical 写道
请问博主在什么资料上看到在5.0后双重检查模式是有效的?

不知道在JE上看到的算不算,忘记是哪篇文章了。
32 楼 褚晓娜(0511) 2010-12-15   引用
我喜欢用第三种,前两天也看了一下关于双重检查锁定的文章,主要还是关注线程的安全问题,http://www.ibm.com/developerworks/cn/java/j-dcl.html我感觉受益匪浅,
31 楼 yanical 2010-12-14   引用
请问博主在什么资料上看到在5.0后双重检查模式是有效的?
30 楼 jssay 2010-12-14   引用
总结得很好,学习了!
29 楼 cantellow 2010-12-13   引用
senton 写道
第四种(饿汉,变种):
如何获取Singleton的实例?

呵呵,你太仔细了,是我的错。我改正。
28 楼 senton 2010-12-13   引用
第四种(饿汉,变种):
如何获取Singleton的实例?
27 楼 rainsilence 2010-12-13   引用
nywyang 写道
只有第7种方法才能在多线程的环境下共享一个实例,就相当于数据库中的序列一样。
其他的单例实现事实上在应用上兵没有多大价值。

双重校验在java上其实没有多大意义。摘自<<java与模式>>

一般单例应该只有4种写法。懒汉,恶汉,枚举和静态内部类。
26 楼 cantellow 2010-12-13   引用
wenxiang_tune 写道
刚刚拜读了peter的精品,深受震撼。

求链接。
25 楼 Total_L 2010-12-13   引用
总结得很好...学习了
24 楼 wenxiang_tune 2010-12-13   引用
刚刚拜读了peter的精品,深受震撼。
23 楼 cantellow 2010-12-12   引用
kane1990 写道
这样没有阻止用new()方法生成新的不同的实例啊,所以还不是单件模式,可以加一句
private Singleton(){

}
将构造函数声明为private类型的,不允许通过new()创建实例,只能通过Singleton.getInstance()方法获得新的实例

你没把文章读完吧。
22 楼 kane1990 2010-12-12   引用
cantellow 写道
maxm 写道
很不错, 不过我觉得是不可以获得克隆实例

嗯,object的克隆方法是不是默认也会调用构造方法,如果是private的,就不会被克隆吧。
如果采用诸如反序列化等等深克隆的话,像枚举或者覆盖readResolve方法就可以避免。
不过这也是一种思路,

用clone方法克隆出来的还是原来的引用
21 楼 kane1990 2010-12-12   引用
这样没有阻止用new()方法生成新的不同的实例啊,所以还不是单件模式,可以加一句
private Singleton(){

}
将构造函数声明为private类型的,不允许通过new()创建实例,只能通过Singleton.getInstance()方法获得新的实例
20 楼 greatwqs 2010-12-12   引用
(*^__^*) 嘻嘻
? 上一页 1 2 下一页 ?
发表评论
完成论坛规则小测验以后,您才能在论坛发帖和回复。



http://phl.iteye.com/
分享到:
评论

相关推荐

    c++单例模式线程日志类

    在C++编程中,单例模式是一种常用的软件设计模式,它保证一个类只有一个实例,并提供一个全局访问点。在这个特定的场景中,我们讨论的是一个实现了单例模式的日志类,该类专为多线程环境设计,具备日志等级控制、...

    C#创建窗体的单例模式

    在IT行业中,编程模式是一种解决常见问题的最佳实践方式,单例模式是其中之一,它确保一个类只有一个实例,并提供一个全局访问点。在C#中,特别是在开发Windows Forms(WinForm)应用程序时,单例模式可以帮助我们更...

    javaweb项目+设计模式(单例模式,工厂模式,动态代理,适配器)

    本项目涵盖了四种常见的设计模式:单例模式、工厂模式、动态代理和适配器模式。下面将详细阐述这四种设计模式的概念、作用以及在实际项目中的应用。 **1. 单例模式** 单例模式是一种限制类实例化次数为一个的设计...

    设计模式之单例模式Java实现和类设计图

    本次我们将深入探讨两种设计模式——单例模式和装饰模式,它们在Java编程中都有着广泛的应用。 首先,让我们来理解“单例模式”。单例模式是一种创建型设计模式,其核心思想是保证一个类只有一个实例,并提供一个...

    ava常用设计模式-单例模式

    ava常用设计模式-单例模式 单例模式(Singleton Pattern)是 Java 中最简单的设计模式之一,有以下特点: 1. 单例类只能有一个实例。 2. 单例类必须自己创建自己的唯一实例。 3. 单例类必须给所有其他对象提供这一...

    第五讲单例模式

    单例模式是设计模式中的重要概念,它在软件开发中被广泛应用,特别是在需要全局唯一实例的场景下。在本讲“第五讲单例模式”中,我们将深入探讨这个模式的原理、实现方式以及其在实际编程中的应用。 单例模式的核心...

    浅议单例模式之线程安全(转)

    单例模式是软件设计模式中的一种经典模式,它限制了类的实例化,确保一个类在整个应用程序中只有一个实例。在多线程环境下,线程安全的单例模式尤为重要,因为如果不正确实现,可能会导致多个线程同时创建多个实例,...

    单例模式和CAS(CompareAndSet).md

    玩转单例模式及比较并交换CAS

    乐在其中设计模式(C#)-单例模式(SingletonPattern)[收集].pdf

    《乐在其中设计模式(C#) - 单例模式(Singleton Pattern)》 单例模式是一种常用的设计模式,其核心思想在于确保一个类在整个应用程序的生命周期中只存在一个实例,并提供一个全局访问点来获取这个唯一的实例。在C#中...

    winform序列化 md5加密 单例模式(无视频).rar

    在C#编程中,"winform序列化 md5加密 单例模式(无视频).rar"这个压缩包文件涵盖了一些核心的编程概念和技术,包括Windows Forms(WinForms)应用程序开发、对象序列化、MD5加密以及单例模式。让我们一一探讨这些知识...

    Example-TypeScript-Singleton-Pattern:TypeScript 和单例模式

    单例模式是软件设计模式中的一种,它保证一个类只有一个实例,并提供一个全局访问点。在 TypeScript 中,我们可以利用静态属性和私有构造函数来实现单例模式,从而确保类的唯一性。以下是对这个"Example-TypeScript-...

    Android设计模式系列之单例模式

    单例模式是软件设计模式的一种,其主要目的是确保一个类只有一个实例,并且提供一个全局访问点来获取这个实例。在Android系统中,单例模式被广泛应用于管理那些需要全局共享的资源,例如输入法管理者...

    设计模式:创建型之原型(深浅拷贝)、单例(饿汉式与懒汉式),三大工厂模式 结构型之适配器模式,代理模式(三大代理)

    创建型模式关注的是对象的创建过程,包括单例模式、原型模式、工厂模式等。单例模式确保一个类只有一个实例,并提供一个全局访问点;原型模式则是通过复制现有的实例来创建新的实例,适用于创建复杂对象或者避免重复...

    Java之单例模式&&NIO

    Java中的单例模式是一种设计模式,它用于保证一个类只有一个实例,并提供一个全局访问点。在给定的代码示例中,我们看到了两种常见的单例实现方式:懒汉式和饿汉式。 **懒汉式单例模式**: 懒汉式单例的特点是延迟...

    Python单例模式的两种实现方法

    Python中的单例模式是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点。这种模式在某些场景下非常有用,比如管理共享资源、配置对象或日志系统等。接下来,我们将详细介绍两种实现Python单例模式的...

    新技术培训 培训讲义9_设计模式之单例适配器和工厂模式.doc

    在这个文档中,我们关注的是三种设计模式:单例模式、适配器模式和工厂模式。 首先,单例模式是一种确保一个类只有一个实例并提供一个全局访问点的设计模式。这种模式的核心在于控制类的实例化过程,确保在任何情况...

    LabviewOOP.rar

    单例模式是一种限制类实例化次数的设计模式,确保一个类只有一个实例,并提供全局访问点。在Labview中,可以通过创建一个私有的构造函数和维护一个类变量来实现单例。当首次请求实例时,创建该类的唯一实例并保存,...

    PHP单例模式简单用法示例

    单例模式是设计模式中的一种,它在软件工程中用于控制类的实例化过程,确保一个类只有一个实例,并提供一个全局访问点。在PHP中,单例模式常用于数据库连接、缓存管理等场景,以避免多次创建和销毁对象带来的性能...

Global site tag (gtag.js) - Google Analytics