基于DCL的单例模式的分析

        最近在看三石-道的关于并发的一些博客,在一篇博客中看到了基于DCL实现的单例模式:

              http://www.molotang.com/articles/407.html

        于是在并发编程网上又看了两篇博客,以加深对单例模式的理解。

        http://ifeve.com/doublecheckedlocking/

        http://ifeve.com/syn-jmm-pre/

        首先总结一下实现单例模式的几种办法:

 

1:最简单,预先初始化了单例的对象,有一定的内存消耗:

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

 

 2:比较常见的,加同步锁:

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

 

3:DCL式(double checking lock),为了避免取得过期对象而想出来的:

public class Singleton { 
private static volatile Singleton instance = null; 
private Singleton() { 
} 
public static Singleton instance() {
if (instance == null) {
synchronized (Singleton.class) { 
if (instance == null) {
instance = new Singleton(); 
} 
} 
} 
return instance; 
} 
}

    DCL式的会有很多问题,在《Java并发编程实践》一书中的16.2.4一节对其进行了说明:问题可能出来线程可能看到部分创建的对象(存在重排的可能性),打算有时间了写个例子验证一下。

 

4:解决了DCL的缺陷,由Bill Pugh提出:不仅实现了延迟加载而且可以实现同步:

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

 

 5:《Effective Java》上推荐的,采用enum:

public enum Singleton { 
  INSTANCE; 
  void show(){System.out.println("Singleton");}
}
 
//客户端调用:
public class Test {
	public static void main(String args[]){
		Singleton.INSTANCE.show();
	}
}

 

      enum和class不同,enum的成员函数和成员变量默认都是public的,而class默认都是private的。

 

6:还可以用线程局部存储来修复DCL,每个线程各自保存一个flag来表示该线程是否执行了同步。

class Foo {
 /** If perThreadInstance.get() returns a non-null value, this thread
	has done synchronization needed to see initialization
	of helper */
	 private final ThreadLocal perThreadInstance = new ThreadLocal();
	 private Helper helper = null;
	 public Helper getHelper() {
		 if (perThreadInstance.get() == null) createHelper();
		 return helper;
	 }
	 private final void createHelper() {
		 synchronized(this) {
			 if (helper == null)
				 helper = new Helper();
		 }
	 // Any non-null value would do as the argument here
		 perThreadInstance.set(perThreadInstance);
	 }
}

   

      但是,如果我们采用序列化/烦序列化的时候,单例可能会产生多个对象。这样单例就失败了。

      当然了,采用enum实现单例的话是不用担心序列化/反序列化的,因为readObject和writeObject两个Serializable接口中的方法对enum是单独处理的。

      如果没有采用enum实现单例,又遇到这个类实现了Seriaalizable接口需要序列化/反序列化的情况也不要担心,只要重写readResolve方法就可以了,在readResolve方法中返回之前对象的引用。

像这样:

public class Singleton implements Serializable { 
private static final Singleton INSTANCE = new Singleton(); 
private Singleton() { 
} 
private Object readResolve() { 
//这个很重要
return INSTANCE; 
} 
public static Singleton getInstance() { 
return INSTANCE; 
} 
}

   当进行反序列化时,  ois.readObject();时内部就是通过反射检查implements Serializable的类有没有readResolve方法,如果有就把readResolve的返回值作为ois.readObject();的返回值. 所以readResolve必须返回之前对象的引用。

在Spring框架中,单例模式是一种常见的设计模式,用于确保在整个应用生命周期中只有一个实例存在。虽然Spring本身已经提供了基于Bean的单例模式支持,但如果你想要手动实现一个简单的Spring风格的单例,你可以按照以下步骤: 1. **静态工厂方法**: - 创建一个私有的构造函数,仅允许通过工厂方法创建实例。 ```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; } } ``` 2. **双重校验锁定(DCL)**: - 使用双重检查锁定优化,避免了同步块导致的性能开销。 ```java public class SingletonDCL { private volatile static Singleton instance; private SingletonDCL() {} public static Singleton getInstance() { if (instance == null) { instance = new SingletonDCL(); } return instance; } } ``` 3. **枚举类型单例**: - 使用枚举类型保证线程安全且易于理解。 ```java public enum SingletonEnum { INSTANCE; private SingletonEnum() {} } ``` 4. **单例模式与Spring结合**: - 在Spring中,`@Singleton`注解或者在配置文件中使用`singleton`属性可以自动实现单例。 相关问题-- 1. 在Spring中,如何通过XML配置实现单例模式? 2. Spring的`@Singleton`注解是如何工作的? 3. 为什么要使用枚举类型实现单例
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值