延迟初始化,双重检查加锁的陷阱

来源:互联网 发布:c语言随机数生成 编辑:程序博客网 时间:2024/05/21 06:26

在初始化类时,有时为了避免不必要的开销,我们会采用延迟初始化的方式来构造类。在多线程的环境中,我们经常会使用双重检查加锁的方式来初始化类,甚至在百度移动应用统计的源码中,我就看到了许多使用这种延迟初始化的方式,如 程序1-1

public class DoubleCheckedLocking {    private static Resource resource;        public static Resource getInstance() {        if(resource == null) {            synchronized(DoubleCheckedLocking.class) {                if(resource == null) {                     resource = new Resource();                }            }        }        return resource;    }}
程序 1-1


这样的代码是存在问题的,对象在没有同步的状态下缺少了Happens-Before的关系,可能出现重排序的问题。

初始化一个新的对象时需要写入多个变量,即新对象中的各个域,同样,在发布一个引用时也需要写入一个变量,即新对象的引用。如果新对象引用的写入操作与对象中各个域的写入操作重排序,如程序1-1,当A线程调用该方法后,这时候resource可能非空,因此B线程在在调用没有同步的判断代码时将直接返回resource,但是这时可能看到Resource实例还未构造完成,因此可能看到某些或全部状态中包含的是无效值(尽管在构造函数中设置的域值似乎是第一次向这些域中写入的值,因此不会有“更旧的”值被视为失效值,但Object的构造函数会在子类构造函数运行之前先将默认值写入所有的域,因此,某个域的默认值可能被视为失效值)。从而调用了一个部分构造的对象,引发错误。

0 0
原创粉丝点击