单例的七种写法

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

 

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. }  
public class Singleton {    private static Singleton instance;    private Singleton (){}    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.     private Singleton (){}  
  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;    private Singleton (){}    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.     private Singleton (){}  
  4.     public static Singleton getInstance() {  
  5.     return instance;  
  6.     }  
  7. }  
public class Singleton {    private static Singleton instance = new Singleton();    private 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.     private Singleton (){}  
  7.     public static Singleton getInstance() {  
  8.     return this.instance;  
  9.     }  
  10. }  
public class Singleton {    private Singleton instance = null;    static {	instance = new Singleton();    }    private 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.     private Singleton (){}  
  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();    }    private 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.     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. }  
public class Singleton {    private volatile static Singleton singleton;    private 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已经没有双重检查锁定的问题了。

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

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

<think>好的,用户想了解如何用代码实现设计模式。我之前学过几种实现方式,但得仔细想想,确保正确性。 首先,模式的核心是确保一个类只有一个实,并提供全局访问点。常见的实现方法有饿汉式、懒汉式、静态内部类和枚举。需要逐一回忆这些方法的代码结构和特点。 饿汉式应该是在类加载时就创建实,线程安全。但可能会造成资源浪费。代码结构记得是用静态变量直接初始化实,私有构造方法,然后提供获取实的方法。 然后是懒汉式,延迟加载,但需要考虑线程安全。早期的懒汉式不加同步的话会有多线程问题,所以可能会用synchronized修饰方法,但这样效率不高。之后可能用双重检查锁来优化,需要配合volatile关键字防止指令重排序。 静态内部类的方式利用了类加载机制,静态内部类在第一次使用时才会加载,从而延迟实化,同时由JVM保证线程安全。记得外部类持有内部类的静态实,通过一个公有方法返回这个实。 枚举方式实现是Effective Java推荐的,因为枚举实的创建默认是线程安全的,且能防止反射和反序列化破坏。代码结构是定义一个枚举类型,里面包含一个实,然后添加所需的方法。 现在需要检查每种方法的代码是否正确。比如饿汉式是否正确使用static和private,构造方法是否私有。懒汉式的双重检查锁是否正确使用volatile和synchronized块。静态内部类是否在内部类中持有实,外部类如何获取。枚举是否只定义一个实,并在其中实现方法。 还要注意引用的问题,用户提供的引用中有提到静态内部类和枚举的实现,所以需要正确关联引用。比如静态内部类对应引用1,枚举对应引用2和3。 接下来需要组织回答的结构,先概述模式,再分点介绍每种方法,给出代码示,并说明优缺点。最后相关问题部分需要生成相关的问题,比如线程安全、优缺点比较、应用场景等。 需要确保代码语法正确,比如Java的类结构,构造方法的私有化,获取实的方法是否为静态。如,饿汉式的代码是否正确,静态内部类是否正确嵌套,枚举是否使用枚举类型。 可能遇到的错误点:双重检查锁是否遗漏volatile导致指令重排问题;静态内部类是否没有正确声明为private static;枚举的是否正确地通过INSTANCE访问。 现在需要验证每种方法的正确性: 1. 饿汉式:实在类加载时创建,构造私有,静态方法返回实。正确。 2. 懒汉式双重检查锁:实用volatile修饰,同步块内双重检查,正确。 3. 静态内部类:内部类持有实,外部类通过getInstance调用,正确。 4. 枚举:枚举类型只有一个实,构造方法私有,通过INSTANCE访问,正确。 确保引用标注正确,比如静态内部类对应引用1,枚举对应引用2和3。在解释每个方法时,在相关部分添加引用标记。 最后,生成相关问题时要考虑用户可能想深入的点,比如各种方法的线程安全、如何防止反射攻击、性能比较等。</think>### 设计模式的代码实现方法 设计模式的核心目标是确保一个类只有一个实,并提供全局访问点。以下是四种常见的实现方式及其代码示: --- #### 1. 饿汉式(线程安全) 在类加载时直接创建实,线程安全但可能造成资源浪费。 ```java public class Singleton { private static final Singleton instance = new Singleton(); private Singleton() {} // 私有构造方法 public static Singleton getInstance() { return instance; } } ``` --- #### 2. 懒汉式(双重检查锁,线程安全) 延迟实化,通过双重检查锁和`volatile`关键字保证线程安全。 ```java public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } ``` --- #### 3. 静态内部类(线程安全) 利用静态内部类的类加载机制实现延迟加载,由JVM保证线程安全[^1]。 ```java public class Singleton { private Singleton() {} private static class Holder { static final Singleton instance = new Singleton(); } public static Singleton getInstance() { return Holder.instance; } } ``` --- #### 4. 枚举实现(线程安全,防反射/反序列化) 枚举天然支持特性,能防止反射攻击和反序列化破坏[^2][^3]。 ```java public enum Singleton { INSTANCE; public void doSomething() { // 具体方法实现 } } // 调用方式:Singleton.INSTANCE.doSomething(); ``` --- ### 不同实现方法的对比 | 方法 | 线程安全 | 延迟加载 | 防反射攻击 | 代码复杂度 | |---------------|----------|----------|------------|------------| | 饿汉式 | 是 | 否 | 否 | 低 | | 懒汉式 | 是 | 是 | 否 | 中 | | 静态内部类 | 是 | 是 | 否 | 低 | | 枚举 | 是 | 否 | 是 | 极低 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值