最终实例变量的安全发布是否可传递给非最终二级引用?

Is the safe publishing of final instance-variables transitive for non-final secondary references?

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

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 的内容是否安全,即使它们包含非最终实例变量?

是的,分配最终字段时会发生冻结操作。你应该阅读 Aleksey Shipilëv's blog it's really useful. He discusses the freeze action semantics in a 2014 blog entry

And here is how it is formally specified. Notice that w may not be the write of final field, and r2 is not the read of the final field. What really matters is that the subchain containing freeze action F, some action a, and r1 which reads the final field — all together make r2 observe w.

Notice two new orders, dereference order, and memory

在博客中,他证明了 final 字段的写入发生在某些操作之前,而这些操作又发生在后续的非 final 字段读取之前 r2

同样在您的示例中,由于您首先构建了一个非共享 NonFinalImmutable,因此最终分配应该冻结之前发生的写入。如果 NonFinalImmutable 可以在外面访问,则所有投注均无效。