`

The "Double-Checked Locking is Broken" Declaration

    博客分类:
  • JAVA
 
阅读更多
http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html


To first explain the desired behavior, consider the following code:


// Single threaded version
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) 
        helper = new Helper();
    return helper;
    }
  // other functions and members...
  }



If this code was used in a multithreaded context, many things could go wrong. Most obviously, two or more Helper objects could be allocated. (We'll bring up other problems later). The fix to this is simply to synchronize the getHelper() method:



// Correct multithreaded version
class Foo { 
  private Helper helper = null;
  public synchronized Helper getHelper() {
    if (helper == null) 
        helper = new Helper();
    return helper;
    }
  // other functions and members...
  }


The code above performs synchronization every time getHelper() is called. The double-checked locking idiom tries to avoid synchronization after the helper is allocated:


// Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) 
      synchronized(this) {
        if (helper == null) 
          helper = new Helper();
      }    
    return helper;
    }
  // other functions and members...
  }


It doesn't work

There are lots of reasons it doesn't work. The first couple of reasons we'll describe are more obvious. After understanding those, you may be tempted to try to devise a way to "fix" the double-checked locking idiom. Your fixes will not work: there are more subtle reasons why your fix won't work. Understand those reasons, come up with a better fix, and it still won't work, because there are even more subtle reasons.

Lots of very smart people have spent lots of time looking at this. There is no way to make it work without requiring each thread that accesses the helper object to perform synchronization.

The first reason it doesn't work


The most obvious reason it doesn't work it that the writes that initialize the Helper object and the write to the helper field can be done or perceived out of order. Thus, a thread which invokes getHelper() could see a non-null reference to a helper object, but see the default values for fields of the helper object, rather than the values set in the constructor.

If the compiler inlines the call to the constructor, then the writes that initialize the object and the write to the helper field can be freely reordered if the compiler can prove that the constructor cannot throw an exception or perform synchronization.

Even if the compiler does not reorder those writes, on a multiprocessor the processor or the memory system may reorder those writes, as perceived by a thread running on another processor.

Doug Lea has written a more detailed description of compiler-based reorderings.
http://gee.cs.oswego.edu/dl/cpj/jmm.html

A fix that doesn't work

Given the explanation above, a number of people have suggested the following code:

// (Still) Broken multithreaded version
// "Double-Checked Locking" idiom
class Foo { 
  private Helper helper = null;
  public Helper getHelper() {
    if (helper == null) {
      Helper h;
      synchronized(this) {
        h = helper;
        if (h == null) 
            synchronized (this) {
              h = new Helper();
            } // release inner synchronization lock
        helper = h;
        } 
      }    
    return helper;
    }
  // other functions and members...
  }


Fixing Double-Checked Locking using Thread Local Storage

Alexander Terekhov (TEREKHOV@de.ibm.com) came up clever suggestion for implementing double checked locking using thread local storage. Each thread keeps a thread local flag to determine whether that thread has done the required synchronization.
 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);
         }
	}

Fixing Double-Checked Locking using Volatile

JDK5 and later extends the semantics for volatile so that the system will not allow a write of a volatile to be reordered with respect to any previous read or write, and a read of a volatile cannot be reordered with respect to any following read or write. See this entry in Jeremy Manson's blog for more details.

// Works with acquire/release semantics for volatile
// Broken under current semantics for volatile
  class Foo {
        private volatile Helper helper = null;
        public Helper getHelper() {
            if (helper == null) {
                synchronized(this) {
                    if (helper == null)
                        helper = new Helper();
                }
            }
            return helper;
        }
    }


// Works with acquire/release semantics for volatile
// Broken under Java 1.4 and earlier semantics for volatile
class Foo {
    private volatile Helper helper = null;
    public Helper getHelper() {
        Helper result = helper;
        if (result == null) {
            synchronized(this) {
                result = helper;
                if (result == null) {
                    helper = result = new Helper();
                }
            }
        }
        return result;
    }

    // other functions and members...
}
Note the usage of the local variable result which seems unnecessary. For some versions of the Java VM, it will make the code 25% faster and for others,


If the helper object is static (one per class loader), an alternative is the initialization on demand holder idiom

// Correct lazy initialization in Java
@ThreadSafe
class Foo {
    private static class HelperHolder {
       public static Helper helper = new Helper();
    }

    public static Helper getHelper() {
        return HelperHolder.helper;
    }
}


This relies on the fact that inner classes are not loaded until they are referenced.
Semantics of final field in Java 5 can be employed to safely publish the helper object without using volatile:[7]

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;
   }
}

分享到:
评论

相关推荐

    C++ and the Perils of Double-Checked Locking

    在介绍双检锁模式(Double-Checked Locking Pattern,DCLP)的C++实现中,Scott Meyers和Andrei Alexandrescu在其2004年的文章中指出,传统的单例模式实现并不具备线程安全性。单例模式是设计模式中经常被提及的一种...

    C++ and the Perils of Double Checked Locking.zip

    《C++ and the Perils of Double Checked Locking》是一篇探讨C++编程中双重检查锁定(Double-Checked Locking)模式潜在问题的文献。在多线程编程中,双重检查锁定是一种常见的优化策略,旨在减少对同步原语的依赖...

    c++ and Peris of Double Checked Locking

    标题:C++与双检查锁定(Double Checked Locking)的陷阱 描述:C++如何解决单例模式的线程安全问题 ### 关键知识点解析: #### 单例模式的线程安全挑战 单例模式是一种设计模式,确保一个类只有一个实例,并提供...

    AngularJS入门教程之ng-checked 指令详解

    本文将针对AngularJS中的ng-checked指令进行详细解析。 ng-checked指令是用于在AngularJS中动态控制复选框(checkbox)或单选按钮(radio)的选中状态。当你需要根据数据模型(model)中某个变量的真值(true)或假值(false...

    深入剖析Java中的双检锁模式:实现、陷阱与最佳实践

    在Java并发编程中,双检锁(Double-Checked Locking)是一种用于减少同步开销的优化技术,尤其适用于懒加载(lazy initialization)的场景。本文将详细探讨双检锁的工作原理、潜在问题以及如何安全地实现它。 双检锁...

    element-ui tree树形控件:default-checked-keys 设置空数组 默认还是选中状态问题

    在这个问题中,开发者遇到了一个关于`default-checked-keys`属性的问题,即当尝试将该属性设置为空数组时,树形控件的节点依然保持选中的状态,这与预期的行为不符。 `default-checked-keys`属性是Element UI Tree...

    深究AngularJS——ng-checked(回写:带真实案例代码)

    具体地,ng-checked指令调用了一个自定义函数isSelected,该函数接收一个id作为参数,如果该id存在于之前选中的结果数组中,则返回true,否则返回false。这样就能够根据之前的用户选择来控制复选框的显示状态。 ...

    Software Adaptation in an Open Environment: A Software Architecture Perspective

    Software Adaptation in an Open Environment: A Software Architecture Perspective by Yu Zhou ...The organization and presentation of the book will be double-checked by professional scholars

    ACE中的DoubleCheckedLocking模式

    北京火龙果软件工程技术中心意图无论什么时候当临界区中的代码仅仅需要加锁一次,同时当其获取锁的时候必须是线程安全的,可以用DoubleCheckedLocking模式来减少竞争和加锁载荷。动机1、标准的单例。开发正确的有效...

    checked.css-超酷CSS3复选框和单选按钮点击动画库

    这里,`checked--effect-name`需要替换为23种动画效果中的一个,如`checked--pulse`或`checked--rotate`。 3. **自定义样式**:除了预设的动画效果,你还可以通过CSS对复选框和单选按钮的样式进行个性化调整,以...

    ng-options和ng-checked在表单中的高级运用(推荐)

    然后,我们可以通过遍历$scope.grade.classes数组,查找is_checked为true的项目,从而得到所有选中的班级。 同样,对于radio,ng-checked的用法类似,只是每个radio对应一个唯一的值,而不是多选的情况。 总结起来...

    C++CLI中实现singleton模式

    双重检测锁(Double-Checked Locking)实现的Singleton模式在多线程应用中有相当的价值。在ACE的实现中就大量使用ACE_Singleton模板类将普通类转换成具有Singleton行为的类。这种方式很好地消除了一些重复代码臭味,...

    设计模式解析-英文

    各种工厂模式 242 第21章 Singleton模式和Double-Checked Locking模式 249 第22章 Object Pool模式 257 第23章 Factory Method模式 267 第24章 工厂模式的总结 272 第八部分 终点与起点 第25章 设计模式回顾:总结与...

    poly-checked:神奇

    多检查奇迹:checked伪类 polyfill(适用于 IE8 及以下)。如何使用包括 jQuery 1.x(与旧 IE 兼容)&gt;= 1.7,然后在 IE 条件注释中包含 poly-checked 脚本: &lt; script src =" //ajax.googleapis....

    Java多线程之延迟初始化1

    synchronized (DoubleCheckedLocking.class) { if (instance == null) { instance = new Instance(); } } } return instance; } } ``` 双重检查锁定的关键在于使用`volatile`关键字修饰`instance`变量。`...

    H5 jQuery多级复选框checkbox选中代码.zip

    代码片段:  function isAllChecked(){ ... allChecked = $(this).find(":checked").not("[name='all-checked']") ,  thisAllCheckBtn = $(this).find(".all-checked [name='all-checked']");

    Java多线程编程环境中单例模式的实现

    为了提高性能,人们提出了**双重检查锁定**(Double-checked locking)的方法。这种方法首先在不加锁的情况下检查`instance`是否为`null`,如果为`null`则进行同步操作: ```java public static Singleton ...

    单例模式简介和java代码实现

    }}在双重检查锁(Double-Checked Locking)实现中,第一次检查是在不加锁的情况下进行的,只有当第一次检查后 instance 仍然为 null 时,才会进入同步代码块进行第二次检查和实例化。这种方式提高了并发性能,因为...

    Java面试.docx

    3. **双端检锁(Double-Checked Locking, DCL)机制**: - DCL是一种尝试提高单例模式效率的设计模式,但如果没有正确同步,可能存在指令重排问题,导致非线程安全。 - 使用volatile可以禁止指令重排,从而在一定...

Global site tag (gtag.js) - Google Analytics