java中的双重校验锁_单例模式双重校验写法及延伸

最近看一些Java内存模型方面的书,讲了一下Java的对象的内存分配过程,其中有个例子讲解多线程锁的问题,说了下面的例子:

单例写法 双重校验写法

//------------------------双重校验锁------------------

private static Singleton singleton2;//-------1

public static Singleton getInstance4() {//------------2

if (singleton2 == null) {//-----------------------3

synchronized (Singleton.class) {//------------4

if (singleton2 == null)//-----------------5

singleton2 = new Singleton();//-------6

}

}

return singleton;

}

问题处在了第6步,Java创建对象的第6步可以分为以下三步:

memory = allocate();//----1

ctorInstance(memory);//-2

instance = memory;//-----3

其中2,3步在JVM编译优化时可能发生重排序,这和采用的JIT有关,并且该重排序遵循intra-thread semantics法则(重排序后不会影响单线程的执行结果)。

如果发生重排序,第3步先于第2步执行,那么A线程可能只是让对象指向内存地址,并没有实质的初始化对象,那么线程B调用时就会发生错误。

解决方案

采用volatile

在Java1.5以后,volatile关键字被加强,这种重排序不允许在多线程中发生。

即在对象声明加上volatile关键字。

实质:禁止编译的重排序。

采用JVM初始化类时加锁

JVM的类的初始化阶段,会获取锁,该锁可以同步多线程对一个类的初始化。

此时衍生一种称为:Initialization On Demand Holder idiom的解决方案。

//-----------------------------静态内部类---------------

private static class SingletonHolder {

private static final Singleton INSTANCE = new Singleton();

}

public static Singleton getInstance3() {

return SingletonHolder.INSTANCE;

}

9cc07280c69c

JVM在多线程中初始化对象过程.jpg

** 实质: **利用JVM的多线程初始化对象的特性,允许重排序,但对其他线程不可见。

另外根据Java语言规范,一个类在一下5种情况会发生初始化:

T是一个类,并且T的实例被创建。

T是一个类,且T中的静态方法被调用

T是一个类,且T中的一个静态字段被赋值。

T是一个类,且T中的非常量字段被使用

T是一个顶级类(TOP Level Class),有断言语句嵌套在T内部被执行。(assert语句,很少用改规则)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值