不安全的发布并发java

vin*_*yag 9 java concurrency multithreading

实践中的Java并发书给出了不安全发布的一个例子

public class Holder 
{
    private int n;
    public Holder(int n)
    {
        this.n = n; 
    }
    public void assertSanity() 
    {
        if (n != n)
            throw new AssertionError("This statement is false.");
    }
}
Run Code Online (Sandbox Code Playgroud)

上面的代码似乎是线程安全的.如果n是公共变量,它将不是线程安全的.这本书的例子是错的吗?

axt*_*avt 10

安全发布是关于内存可见性的.内存可见性的概念比其他线程安全问题(例如竞争条件)有点棘手.

当一个线程按特定顺序执行的操作看起来以不同的顺序执行另一个线程时,会出现内存可见性问题(这可能是由编译器或CPU进行的优化引起的).

在你的情况下:

// Thread A
h = new Holder(42);

// Thread B
h.assertSanity();
Run Code Online (Sandbox Code Playgroud)

对于线程A,n肯定是在之前初始化的h.

但是由于缺乏安全发布,因此线程B不能保证相同.线程B可能h在初始化状态下看到,但是n不会被初始化.此外,n线程B所观察到的状态可能在评估期间发生变化n != n,从而导致assertSanity()异常.

请注意,在所有情况下都不会发生此问题.您可能永远不会看到这种情况发生,但Java Memory Model在这种情况下并不保证正确性.

  • +1为此,您必须在不触及任何读/写障碍的情况下传递对象.即如果您使用线程安全集合或volatile字段,则不会发生这种情况. (3认同)
  • 我非常肯定(需要查看)在构造函数中进行的赋值总是对其他线程可见.所以当从线程B观察时,`n`将始终被初始化 (2认同)