最终实例变量的安全发布是否适用于非最终二次引用?

时间:2015-12-17 18:11:52

标签: java multithreading thread-safety final java-memory-model

我知道在构造函数完成后,最终的实例变量会安全地发布到所有线程。但是,我想知道这是否仍然安全,如果最终的实例变量包含对包含非最终实例变量的对象的引用。构造函数完成后,永远不会更改此辅助非最终实例变量。请考虑以下示例:

public class NonFinalImmutable {
    private Iterable<String> list = Collections.unmodifiableList(Arrays
            .asList("foo", "bar", "foobar"));

    public Iterable<String> getList() {
        return list;
    }
}

public class FinalImmutable {
    private final NonFinalImmutable reference;
    private final String[] array;

    public FinalImmutable(NonFinalImmutable reference,
            String... arrayEntries) {
        this.reference = reference;
        this.array = arrayEntries;
    }

    public NonFinalImmutable getReference() {
        return reference;
    }

    public String[] getArray() {
        return array;
    }
}

private void execute() {
    new Thread() {
        @Override
        public void run() {
            useLater(construct());
        }
    }.start();
}

private FinalImmutable construct() {
    return new FinalImmutable(new NonFinalImmutable(), "asdf", "jklö");
}

private void useLater(FinalImmutable finalImmutable) {
    new Thread() {
        @Override
        public void run() {
            for (String s : finalImmutable.getReference().getList()) {
                System.out.println(s);
            }
            System.out.println();
            for (String s : finalImmutable.getArray()) {
                System.out.println(s);
            }
        }
    }.start();
}

在另一个线程中使用实例变量FinalImmutable.referenceFinalImmutable.array的内容是否安全,即使它们包含非最终实例变量?

1 个答案:

答案 0 :(得分:3)

是的,在分配最终字段时会发生冻结操作。你应该阅读Aleksey Shipilëv's blog它真的很有用。他讨论了2014 blog entry

中的冻结动作语义
  

这是正式指定的方式。请注意,w可能不是最终字段的写入,而r2不是最终字段的读取。真正重要的是包含冻结动作F的子链,一些动作a和读取最终场的r1 - 一起使r2观察到w。

     

注意两个新订单,取消引用订单和内存

在博客中,他证明最后一个字段的写入发生在某个动作之前,而这个动作又发生在随后的非最终字段读取r2之前。

同样在您的示例中,由于您首先构造非共享NonFinalImmutable,因此最终分配应冻结先前发生的写入。如果NonFinalImmutable可以在外面访问,则所有投注均已关闭。