`

基于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必须返回之前对象的引用。

分享到:
评论

相关推荐

    单例模式(饿汉模式、懒汉模式、DCL单例模式、枚举)

    饿汉模式、懒汉模式、DCL单例模式、枚举;不同情况下使用不同的单例创建模式

    设计模式经典案例之单例模式

    单例模式主要有懒汉式和饿汉式两种实现,饿汉式不会有线程安全的问题,但是提前构造对象占用了一定的资源,如果对内存要求较低的场景可以使用饿汉式实现;懒汉式应使用DCL机制来避免多线程竞争资源的问题,并且懒汉...

    java单例模式看这一篇就够了

    深入分析java单例模式什么是单例模式单例模式的常见写法一、饿汉式单例优点缺点示例二、懒汉式单例示例1(普通写法)示例2(synchronized写法)示例3(DCL写法)示例4(内部类写法)三、注册式单例示例1(容器式)示例2(枚举式...

    单例模式详解

    Singleton模式可以相当的复杂,比头五种模式加起来还复杂,譬如涉及到DCL双锁检测(double checked locking)的讨论、涉及到多个类加载器(ClassLoader)协同时、涉及到跨JVM(集群、远程EJB等)时、涉及到单例对象...

    一个单例模式的晋级过程(饿汉-懒汉-DCL-IoDH-枚举)

    一个单例模式的晋级过程(饿汉-懒汉-DCL-IoDH-枚举) 文章目录一个单例模式的晋级过程(饿汉-懒汉-DCL-IoDH-枚举)什么是单例?单例有哪些运用场景?实现1.饿汉式测试优化-final2.懒汉式优化-加锁同步3.DCL双检锁/双重...

    设计模式第二天学习内容

    懒汉式单例模式的实现方式有三种: * 双重检查锁方式(DCL) 12345678910111213 单例模式: 懒汉式:延迟加载方式 饿汉式:立即加载 面试绝对面的都是懒汉式的问题。 单例模式如果使用不当,就容易引起线程安全问题...

    Android设计模式之单例模式详解

    单例模式 一个类只有一个实例,并且可以全局访问使用 应用场景 如账户管理类,数据库操作类等(某个对象频繁被访问使用) 常用方式 饿汉式 懒汉式 同步加锁 DCL双重加锁验证 静态内部类 枚举单例 饿汉式 加载类的...

    直接将VBForm转DCL 程序【VB2DCL】

    用VB的Form直接生成DCL对话框程序极其调用的LSP文件 能自动对DCL对话框中的参数进行读取和赋值,并输出 使用步骤: 一、在VB中绘制好表单Form 注意:排列的时候尽量一行的Top坐标相同,这样能保证在同一行 二、将...

    基于AutoCAD的DCL与AutoLISP的有机整合

    分析了用AutoCAD内嵌的AutoLISP与DCL整合的开发过程,通过开发表面结构代号绘制工具的实例,说明了利用DCL和AutoLISP能够编写出人机交互强和功能强大的程序,从而对AutoCAD进行有效的二次开发,提高绘图效率,使AutoCAD...

    DCL对话框自学教程

    一个学习DCL对话框编程的好教程,适合于初级学者。

    AutoLISP_DCL.

    AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.AutoLISP_DCL.

    AutoCAD完全应用指南—AutoLISP DCL Visual LISP程序设计篇(随书光盘)

    另外介绍了新手常犯错误分析及应对技巧。第4篇(第22~35章)为visuallisp程序设计应用与整合,主要介绍visual lisp的基本环境、重要函数和程序调试;还介绍了autocad activex对象控制以及visual lisp在e2d、3d、文字...

    DCL对话框自学教程_dcl_grabbedjpr_autocad_

    AutoCAD二次开发之DCL对话框自学教程

    DCL常用设计方法

    dcl常用的一些设计方法 主要是大量的一些实例

    DCL-33A资料说明书

    `1111111111111111111111111111111111111111111

    DCL实验课程终极报告

    针对DCL专业课程提供期末报告的模板级样例

    数据库实验报告 SQL语言的DCL (详细版 含有截图的)

    本次实验了解DCL语言的GRANT和REVOKE语句对数据库存取权限的控制,学会SQL Server 2000的查询分析器中用DCL语言对数据库存取权限进行设定。 本实验需要2学时。 二、实验内容 1.创建新的数据库用户 2.用GRANT语句对...

Global site tag (gtag.js) - Google Analytics