Java多线程环境下的对象构造

5

我正在阅读一本名为《Java并发实践》的书,作者给出了一个不安全的对象发布的例子。以下是例子:

public Holder holder;

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

并且

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.");
    }
}

这是否意味着当一个对象还没有完全构造时,其他线程就可以访问它?我猜当线程A调用holder.initialize();并且线程B调用holder.assertSanity();时,如果线程A尚未执行this.n = n;,则条件n != n将不会被满足。

这是否也意味着如果我有一个更简单的代码,例如:

int n;

System.out.println(n == n); //false?
3个回答

3
如果 assertSanity 方法在第一次和第二次加载 n 之间被抢占,则可能会出现问题(第一次加载将看到 0,并且第二次加载将看到构造函数设置的值)。问题在于基本操作是:
  1. 为对象分配空间
  2. 调用构造函数
  3. 将 holder 设置为新实例
编译器/JVM/CPU 允许重新排序步骤 #2 和 #3,因为没有内存屏障(final、volatile、synchronized 等)。
从您的第二个示例中,不清楚 "n" 是局部变量还是成员变量,或者另一个线程如何同时对其进行修改。

1

您的理解是正确的。这正是作者试图阐述的问题。在涉及多个线程时,Java中没有保护确保对象在访问之前完全构建的卫兵。Holder不是线程安全的,因为它包含可变状态。需要使用同步来修复此问题。

我不确定我理解您的第二个例子,因为缺乏上下文。


0
public static void main(String[] args) {

    A a = new A();
    System.out.println(a.n);

}

static class A{
    public int n;

    public A(){

        new Thread(){

            public void run() {
                System.out.println(A.this.n);
            };

        }.start();

        try {
            Thread.currentThread().sleep(1000);
            n=3;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

这个例子的结果是“0 3”,这意味着在对象的构造函数完成之前,另一个线程可能会使用对该对象的引用。您可以在此处找到其余答案。希望能有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接