java 双重检查锁 失效_关于多线程:为什么Java中双重检查锁定被打破?

此问题与旧Java版本的行为以及双重检查锁定算法的旧实现有关

较新的实现使用volatile并依赖于稍微改变的volatile语义,因此它们没有损坏。

声明字段分配始终是原子的,除了long或double字段。

但是,当我阅读解释为什么双重检查锁定被破坏时,据说问题出在赋值操作中:

// 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...

}

Thread A notices that the value is not initialized, so it obtains the

lock and begins to initialize the

value.

Due to the semantics of some programming languages, the code

generated by the compiler is allowed

to update the shared variable to point

to a partially constructed object

before A has finished performing the

initialization.

Thread B notices that the shared variable has been initialized (or so

it appears), and returns its value.

Because thread B believes the value is

already initialized, it does not

acquire the lock. If B uses the object

before all of the initialization done

by A is seen by B (either because A

has not finished initializing it or

because some of the initialized values

in the object have not yet percolated

to the memory B uses (cache

coherence)), the program will likely

crash.

(from http://en.wikipedia.org/wiki/Double-checked_locking).

什么时候有可能? 在64位JVM上分配操作是否可能不是原子的?

如果否,那么"双重检查锁定"是否真的无效?

在64位JVM上没有区别。

stackoverflow.com/questions/12448864/的可能重复项

问题不是原子性,而是排序。只要不违反之前发生的情况,就允许JVM对指令进行重新排序以提高性能。因此,运行时理论上可以在执行来自类Helper的构造函数的所有指令之前调度更新Helper的指令。

它总是排序,不是吗:)。那就是正确的答案

引用的分配是原子的,但构造不是!因此,如解释中所述,假设线程B希望在线程A完全构造单例之前使用它,它不能创建新实例,因为引用不为null,因此它仅返回部分构造的对象。

If you do not ensure that publishing

the shared reference happens before

another thread loads that shared

reference, then the write of the

reference to the new object can be

reordered with the writes to its

fields. In that case, another thread

could see an up-to-date value for the

object reference but out of date

values for some or all of the object's

state - a partially constructed

object. -- Brian Goetz: Java Concurrency in Practice

由于对null的初始检查未同步,因此没有发布,因此可以重新排序。

java是否先声明了赋值然后才计算正确的部分的地方? (它是Future模式的一种,但我从未见过有关Java编译器使用它的官方声明)。

@罗马:" Java先分配然后才计算正确的部分"不是一件简单的事情。我认为那是不正确的说法。 Java允许低级代码以某种方式对语句进行重新排序,从而在另一个线程访问不是volatile或final的共享字段时导致意外结果。

@罗马看到更新的答案...

那很有意思。

Java中的双重检查锁定存在多种问题:

http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html

对于Java 5+,如果您声明将字段初始化为volatile(当然,如该论文中所述),它将按预期工作,不会出现问题。

在构造器内部构造Helper的实例可能需要几个分配,并且语义允许相对于分配helper = new Helper()将它们重新排序。

因此,字段Helper可能被分配了对对象的引用,在该对象中并非所有分配都已发生,因此该对象未完全初始化。

您是什么意思"几个作业"?我只看到1:helper = new Helper()。如果可以"就地"构造它,那么您可以提供一个链接来证明java realy确实如此奇怪吗?谢谢。

@Roman:如您的引号中所述,允许编译器生成的代码将Helper分配给尚未完成构造的Helper实例。

阅读本文:http://www.javaworld.com/jw-02-2001/jw-0209-double.html

即使您不了解所有细节(像我一样),也请相信这个好窍门不起作用。

对不起,这可能与问题无关,我很好奇。

在这种情况下,在分配和/或返回值之前获取锁会更好吗?喜欢:

private Lock mLock = new ReentrantLock();

private Helper mHelper = null;

private Helper getHelper() {

mLock.lock();

try {

if (mHelper == null) {

mHelper = new Helper();

}

return mHelper;

}

finally {

mLock.unlock();

}

}

还是使用双重检查锁定有任何优势?

/*Then the following should work.

Remember: getHelper() is usually called many times, it is BAD

to call synchronized() every time for such a trivial thing!

*/

class Foo {

private Helper helper = null;

private Boolean isHelperInstantiated;

public Helper getHelper() {

if (!isHelperInstantiated) {

synchronized(this) {

if (helper == null) {

helper = new Helper();

isHelperInstantiated = true;

}

}

}

return helper;

}

// other functions and members...

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值