不是线程安全的对象发布 [英] Not thread safe Object publishing

查看:117
本文介绍了不是线程安全的对象发布的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在实践中阅读Java并发,第3.5节:声明

Reading Java concurrency in practice, section 3.5: Claim is raised that

public Holder holder;
public void initialize() {
     holder = new Holder(42);
}

除了明显的线程安全地创建2个持有人实例的危险,可能会出现发布问题,对于Holder类,例如

Besides the obvious thread safely hazard of creating 2 instances of Holder the book claims a possible publishing issue can occur, further more for a Holder class such as

public Holder {
    int n;
    public Holder(int n) { this.n = n };
    public void assertSanity() {
        if(n != n)
             throw new AssertionError("This statement is false.");
    }
}

可以抛出AssertionError!

an AssertionError can be thrown !

这怎么可能?我唯一可以想到的是,这可以允许这样的可笑行为是如果持有者构造函数不会阻塞,所以引用将是创建到实例,而构造函数代码仍然运行在不同的线程。这是可能的吗?

How is this possible ? The only this I can think of that can allow such ridiculous behavior is if the Holder constructor would not be blocking, so a reference would be creates to the instance while the constructor code still runs in a different thread. Is this possible ?

推荐答案

这是可能的原因是Java有一个弱内存模型。它不保证读/写的排序。这个特殊的问题可以通过代表2个线程的以下2个代码片段来重现。

The reason why this is possible is that Java has a weak memory model. It does not guarantee ordering of read / writes. This particular problem can repro with the following 2 code snippets representing 2 threads

线程1:

someStaticVariable = new Holder(42);

主题2:

someStaticVariable.assertSanity(); // can throw

在表面上看起来不可能发生这种情况。为了理解为什么会发生这种情况,你必须通过Java语法,并降低到更低的水平。如果你查看线程1的代码,它基本上可以分解为一系列的内存写入和分配

On the surface it seems impossible that this could ever occur. In order to understand why this can happen you have to get past the Java syntax and get down to a much lower level. If you look at the code for thread 1, it can essentially be broken down into a series of memory writes and allocations


  1. 将内存分配给指针1

  2. 在偏移0处将指针42写入指针1.

  3. 将指针1写入someStaticVariable

因为Java的内存模型很弱,所以从thread2的角度来看,代码实际执行顺序是完全可能的。

Because Java has a weak memory model it is perfectly possible for the code to actually execute in the following order from the perspective of thread2.


  1. 将指针1分配给指针1

  2. 将指针1写入someStaticVariable

  3. 将指针1写入偏移量0

可怕?是的,但它可以发生。

Scary? Yes but it can happen.

这意味着,Thread2现在可以调用到assertSanity之前n获得值42.值n可能是在assertSanity期间读取两次,在操作#3完成之前和之后一次,因此看到2个不同的值并抛出异常。

What this means though is that Thread2 can now call into assertSanity before n has gotten the value 42. It is possible fro the value n to be read twice during assertSanity, once before operation #3 completes and once after and hence see 2 different values and throw an exception.

编辑

Jon说这是不可能的版本的Java,由于更新到内存模型。

According to Jon this is not possible (thankfully) with newer versions of Java due to updates to the memory model.

编辑第二

Jon说,他从来不说不可能第8版本的Java,除非字段是final。

According to Jon, he never says that is impossible with the 8th version of Java unless the field is final.

这篇关于不是线程安全的对象发布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆