如何在Java中使用双重检查锁实现单例
2014-08-28 16:46
288 查看
单例类在Java开发者中非常常用,但是它给初级开发者们造成了很多挑战。他们所面对的其中一个关键挑战是,怎样确保单例类的行为是单例?也就是说,无论任何原因,如何防止单例类有多个实例。在整个应用生命周期中,要保证只有一个单例类的实例被创建,双重检查锁(Double checked locking of Singleton)是一种实现方法。顾名思义,在双重检查锁中,代码会检查两次单例类是否有已存在的实例,一次加锁一次不加锁,一次确保不会有多个实例被创建。顺便提一下,在JDK1.5中,Java修复了其内存模型的问题。在JDK1.5之前,这种方法会有问题。本文中,我们将会看到怎样用Java实现双重检查锁的单例类,为什么Java
5之前的版本双重检查锁会有问题,以及怎么解决这个问题。顺便说一下,这也是重要的面试要点,我曾经在金融业和服务业的公司面试被要求手写双重检查锁实现单例模式、相信我,这很棘手,除非你清楚理解了你在做什么。你也可以阅读我的完整列表“单例模式设计问题”来更好的准备面试。
一个常见情景,单例类在多线程环境中违反契约。如果你要一个新手写出单例模式,可能会得到下面的代码:
然后,当你指出这段代码在超过一个线程并行被调用的时候会创建多个实例的问题时,他很可能会把整个
checked locking pattern),一种只在临界区代码加锁的方法。程序员称其为双重检查锁,因为会有两次检查
private volatile static Singleton _instance;
这个方法表面上看起来很完美,你只需要付出一次同步块的开销,但它依然有问题。除非你声明
relationship)。对于
5之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-before guarantee),你可以安全地假设其会工作良好。另外,这不是创建线程安全的单例模式的最好方法,你可以使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全。另一种方法是使用静态持有者模式(static
holder pattern)。
5之前的版本双重检查锁会有问题,以及怎么解决这个问题。顺便说一下,这也是重要的面试要点,我曾经在金融业和服务业的公司面试被要求手写双重检查锁实现单例模式、相信我,这很棘手,除非你清楚理解了你在做什么。你也可以阅读我的完整列表“单例模式设计问题”来更好的准备面试。
为什么你需要双重检查锁来实现单例类?
一个常见情景,单例类在多线程环境中违反契约。如果你要一个新手写出单例模式,可能会得到下面的代码:getInstance()方法设为同步(synchronized),就像我们展示的第二段示例代码
getInstanceTS()方法一样。尽管这样做到了线程安全,并且解决了多实例问题,但并不高效。在任何调用这个方法的时候,你都需要承受同步带来的性能开销,然而同步只在第一次调用的时候才被需要,也就是单例类实例创建的时候。这将促使我们使用双重检查锁模式(double
checked locking pattern),一种只在临界区代码加锁的方法。程序员称其为双重检查锁,因为会有两次检查
_instance == null,一次不加锁,另一次在同步块上加锁。这就是使用Java双重检查锁的示例:
这个方法表面上看起来很完美,你只需要付出一次同步块的开销,但它依然有问题。除非你声明
_instance变量时使用了volatile关键字。没有
volatile修饰符,可能出现Java中的另一个线程看到个初始化了一半的
_instance的情况,但使用了
volatile变量后,就能保证先行发生关系(happens-before
relationship)。对于
volatile变量
_instance,所有的写(write)都将先行发生于读(read),在Java
5之前不是这样,所以在这之前使用双重检查锁有问题。现在,有了先行发生的保障(happens-before guarantee),你可以安全地假设其会工作良好。另外,这不是创建线程安全的单例模式的最好方法,你可以使用枚举实现单例模式,这种方法在实例创建时提供了内置的线程安全。另一种方法是使用静态持有者模式(static
holder pattern)。
相关文章推荐
- 如何在Java中使用双重检查锁实现单例
- 如何在Java中使用双重检查锁实现单例
- 如何在Java中使用双重检查锁实现单例
- Java使用double check(双重检查)实现单例模式的一个小细节
- 使用java语言操作,如何来实现MySQL中Blob字段的存取
- easyui datagrid中想使用showFooter在java中如何实现
- 3.第三单元任务二实训:使用作业提交系统提交Java作业时 ,需要输入Java源代码文件名和自己的邮箱,提交前对Java文件名以及邮箱进行有效检查。编写程序实现对输入的Java源文件名以及邮箱有效性的
- 使用Jquery+Ajax+Json如何实现分页显示附JAVA+JQuery实现异步分页
- 如何使用网页开发自己的app,在网页中的按钮与自己的java代码绑定来实现打电话即javascript代码调用java代码,和java代码来调用javascript代码
- Java中如何使用组件实现文件上传下载
- Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP
- Java 实现自定义异常 以及如何使用该异常
- Java_JSP2_使用URL Rewrite如何实现网站伪静态?
- java中如何实现检查一个数是不是2的幂次方
- 使用JAVA如何对图片进行格式检查以及安全检查处理
- Java初学者如何迈出AOP第一步--使用Java 动态代理实现AOP
- java学习——如何实现线程之间的通信 ,Condition 的使用
- 如何使用java程序实现windows锁屏
- 使用JAVA如何对图片进行格式检查以及安全检查处理
- 如何使用JNI,实现Java本地编程