Double check lock模式的典型代码:
public class Singleton {
private static Singleton instance = null;
private Singleton() {
}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
理论上来将,DCL模式能够以优异的性能解决单例模式在多线程下的并发问题,但是实际情况下,因为编译器会采用代码重排(instruction reorder)的优化手段,从而导致偶尔出现返回一个未完全初始化(内存已经分配,reference对象已经创建并且指向创建的内存地址,但是构造函数还没有开始执行或者未完全执行完毕)的Singleton实例并引起程序异常。
为了解决这个问题,JDK1.5以及之后的版本,可以通过volatile以及final关键字来修正DCL中的问题:
volatile比较简单,只需要在instance变量定义前加上volatile关键字即可,由于使用volatile关键字会禁止JVM的代码重排,因此可以解决DCL中存在的问题。
使用final关键字稍微有些地方需要注意,先看代码:
public class FinalWrapper<T> {
public final T value;
public FinalWrapper(T value) {
this.value = value;
}
}
public class Foo {
private FinalWrapper<Helper> helperWrapper = null;
public Helper getHelper() {
FinalWrapper<Helper> wrapper = helperWrapper;
if (wrapper == null) {
synchronized(this) {
if (helperWrapper == null) {
helperWrapper = new FinalWrapper<Helper>(new Helper());
}
wrapper = helperWrapper;
}
}
return wrapper.value;
}
}
首先实例变量value被声明为了final,但是比较特别的一点是在Foo.getHelper()方法中,首先定义了wrapper这个局部变量,并将helperWrapper赋给了它,然后判断wrapper是否为空再进行后续的处理。必须使用wrapper的原因是因为做为局部变量,wrapper不存在线程不安全的问题,如果直接使用helperWrapper,因为之前代码重排的原因,仍然有可能得到一个不完全初始化的对象实例。
再回到final关键字的语义,在JDK1.5以及之后的版本中,编译器能够保证被final关键字定义的属性在对象实例化中能够被正确的初始化(非final对象因为代码重排的原因可能不能满足这个条件),因此在最后一段程序中,wrapper.value永远能够被正确的初始化。
补充1:DCL是在Singleton模式中最容易被使用的,目的是为了解决Singleton在多线程下的线程安全问题。其实为了解决这个问题,除去上述方法之外,还有一些比较直接的方式,例如直接在声明Singleton instance时就将它初始化(无法做到延时加载)或者在getInstance()方法上加入synchronized关键字(性能问题),比较有趣的是下面这种通过内部类来实现延时加载(原理是利用了内部类直到被实际引用时才会加载的机制):
class Foo {
private static class HelperHolder {
public static Helper helper = new Helper();
}
public static Helper getHelper() {
return HelperHolder.helper;
}
}
补充2:关于volatile关键字,volatile变量保证在各个工作线程内存之间的一致性,但是不代表任何情况下基于volatile变量的操作都是线程安全的(在操作依赖于当前变量值并且会修改它的情况下,因此我们仍然需要使用DCL来保证单例的唯一性)。同时volatile变量保证了代码顺序的不变性,但是在和其他非volatile变量一起使用时,仍然会触发编译器的代码重排。
分享到:
相关推荐
private static final Singleton INSTANCE = new Singleton(); private Singleton() {} public static Singleton getInstance() { return INSTANCE; } } ``` 2. **懒汉模式**: 懒汉模式是在首次调用 `...
为了确保有序性,我们需要使用`synchronized`关键字或者`volatile`配合`final`字段来构造线程安全的单例。 在DCL模式中,`volatile`关键字的第一次检查可以避免不必要的同步,而第二次检查加上`synchronized`块确保...
在实际编程中,volatile常用于单例模式的双重检查锁定(DCL)、发布静态初始化字段、线程中断标志等场景,以保证线程间的数据同步和正确性。 总结,volatile是Java并发编程中不可或缺的一部分,它提供了一种相对轻...
#### 2.1 DCL 单例是否需要加 volatile? **双重检查锁定(Double-Checked Locking, DCL)**是一种用于创建线程安全的单例模式的方法。然而,在Java中如果没有正确使用`volatile`,则可能导致DCL单例出现异常。这是...
在这个DCL实现中,volatile关键字确保了多线程环境下对instance的可见性和有序性,防止出现线程间的脏读问题。这里的关键在于两次检查instance是否为null,第一次是为了避免不必要的同步,第二次是在同步区域内创建...
正确的DCL实现需要使用volatile关键字: ```java public class DoubleCheckSingleton { private volatile static DoubleCheckSingleton instance; private DoubleCheckSingleton() {} public static ...
**正文** Java单例模式是一种常用的软件设计模式...需要注意的是,Java 5之后的版本,由于JVM对volatile的优化,DCL式已经能够很好地解决线程安全问题。在Java的并发编程中,正确理解和运用单例模式是非常重要的技能。
它在instance为null时才创建实例,并通过volatile关键字保证了多线程环境下的可见性和有序性。 ```java public class Singleton { private volatile static Singleton instance; private Singleton() {} public ...
它要求可见性(线程看到其他线程修改过的变量值)、有序性(限制指令重排序),并定义了锁、volatile、final规则来保证这些特性。 【单例模式】 单例模式是一种设计模式,保证一个类只有一个实例。在Java中,DCL...
DCL提高了性能,但涉及volatile关键字;静态内部类利用类加载机制,避免同步;枚举方式最简洁,且线程安全。 在实际开发中,根据项目需求和性能考虑,可以选择适合的单例模式实现。例如,如果资源消耗不是问题,...
在Java中实现单例模式有多种方法,包括懒汉式、饿汉式、双重检查锁定(DCL)、静态内部类和枚举。每种方法都有其优缺点,适用于不同的场景。 1. **懒汉式**: 懒汉式单例的特点是延迟初始化,即只有当第一次需要时...
在Java中实现单例模式有多种方法,包括懒汉式、饿汉式、双重检查锁定(DCL)以及枚举方式。下面我们将详细讨论这些实现方式及其优缺点。 1. **懒汉式**: 懒汉式单例的特点是在类第一次被请求时才进行实例化,延迟了...
双重检查锁定(Double-Checked Locking, DCL)技术在懒汉式的同步基础上做了优化,只在第一次实例化时进行同步操作,避免了每次调用都同步带来的性能损耗。需要注意的是,这里使用了 `volatile` 关键字来确保可见性...
DCL模式通过`volatile`关键字确保了多线程环境下的可见性和有序性,同时避免了不必要的同步开销。 设计模式的目的是提高代码的可读性、可维护性和复用性。单例模式在许多场景下非常有用,如数据库连接池、缓存管理...
在Java中实现单例模式有多种方法,包括懒汉式、饿汉式、双重检查锁定(DCL)、静态内部类以及枚举类型等。每种方式都有其优缺点,适用场景也不同。 1. **懒汉式**:延迟初始化,只有在第一次调用getInstance()方法...
例如,在单例模式的实现中,通常采用双重检查锁定(Double-Checked Locking, DCL)模式,但如果不恰当地使用`volatile`关键字,可能会导致线程安全问题。具体来说,如果`Singleton`对象在尚未完全构造完成时被另一个...
利用类加载机制保证线程安全,延迟加载,同时避免了DCL的volatile问题。 ```java public class Singleton { private Singleton() {} private static class SingletonHolder { private static final Singleton ...
private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { ...
private volatile static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { ...