首页 > 编程语言 > 详细

JAVA并发编程的艺术读书笔记

时间:2019-08-19 18:26:52      阅读:84      评论:0      收藏:0      [点我收藏+]

双重检查锁定与延迟初始化

在Java多线程程序中,有时候需要采用延迟初始化来降低初始化类和创建对象的开销。双重检查锁定是常见的延迟初始化技术,但它是一个错误的用法

非线程安全的延迟初始化对象

package 双重检查锁定与延迟初始化;
//非线程安全的延迟初始化对象
public class UnsafeLazyInitialization {
    private static UnsafeLazyInitialization instance;

    public static UnsafeLazyInitialization getInstance(){
        if(instance == null)//代码1
            instance = new UnsafeLazyInitialization();//代码2
        return instance;
    }
}

在上面的类中,假设线程A执行代码1的同时,B线程执行代码2。此时,线程A可能会看到instance引用的对象还没有完成初始化。

线程安全的延迟初始化

package 双重检查锁定与延迟初始化;
//线程安全的延迟初始化,由于对getInstance方法做了同步处理,sychronized将导致性能开销
public class SafeLazyInitialization {
    private static SafeLazyInitialization instance;

    public synchronized static SafeLazyInitialization getInstance(){
        if(instance == null)
            instance = new SafeLazyInitialization();
        return instance;
    }
}

由于对getInstance方法做了同步处理,synchronized将导致性能开销。getInstance方法如果被多个线程频繁调用,将导致程序执行性能的下降。

双重检查锁定

public class DoubleCheckedLocking {
    private static DoubleCheckedLocking instance;

    public static DoubleCheckedLocking getInstance(){
        if(instance == null){//1处
            synchronized(DoubleCheckedLocking.class){
                if(instance == null)
                    instance = new DoubleCheckedLocking();//2处
            }
        }
        return instance;
    }
}

在A线程位于同步代码块2处,初始化对象(但还未初始化完成的时候),有可能有另外的线程B运行到1处。此时instance检查不为null,但instance返回的却不是一个完整的对象。

为什么instance没有被A真正初始化的时候,其指向不为null呢?原因在于编译器的指令重排序

创建一个对象的过程可由如下三步伪代码表示:

1.memory = allocate() //分配对象的内存空间

2.ctorInstance(memory) //初始化对象

3.instance = memory //设置instance指向分配的内存地址

上面的创建对象的过程是在一个单线程中完成的, JMM保证了重排序不会改变单线程内的程序执行结果。换句话说,JAVA允许那些在单线程内,不会改变单线程程序执行结果对的指令重排序。上述三个步骤在一些JIT编译器上被单线程执行的时候,为了提高程序的执行性能,有可能会被重排序且不影响单线程下的执行结果:

1.memory = allocate() //分配对象的内存空间

2.instance = memory //设置instance指向分配的内存地址

3.ctorInstance(memory) //初始化对象

这样,instance 在单线程内会先被指向内存分配地址(此时检查instance不为null),而后这个地址内才真正存放初始化完成的对象数据。这样线程A执行到步骤2,将instance指向内存地址以至于对instance作检查不为null的时候(但实际上该地址内没有初始化完成的对象数据),线程B运行到了程序的1处,对instance作了不为null的检查后,直接返回了这个“虚有其表”的instance。后续程序如果用到了这个对象,必然导致程序的错误。

JAVA并发编程的艺术读书笔记

原文:https://www.cnblogs.com/greatLong/p/11378733.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!