我知道在构造函数完成后,最终的实例变量会安全地发布到所有线程。但是,我想知道这是否仍然安全,如果最终的实例变量包含对包含非最终实例变量的对象的引用。构造函数完成后,永远不会更改此辅助非最终实例变量。请考虑以下示例:
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.reference
和FinalImmutable.array
的内容是否安全,即使它们包含非最终实例变量?
答案 0 :(得分:3)
是的,在分配最终字段时会发生冻结操作。你应该阅读Aleksey Shipilëv's blog它真的很有用。他讨论了2014 blog entry
中的冻结动作语义这是正式指定的方式。请注意,w可能不是最终字段的写入,而r2不是最终字段的读取。真正重要的是包含冻结动作F的子链,一些动作a和读取最终场的r1 - 一起使r2观察到w。
注意两个新订单,取消引用订单和内存
在博客中,他证明最后一个字段的写入发生在某个动作之前,而这个动作又发生在随后的非最终字段读取r2
之前。
同样在您的示例中,由于您首先构造非共享NonFinalImmutable
,因此最终分配应冻结先前发生的写入。如果NonFinalImmutable
可以在外面访问,则所有投注均已关闭。