标题为“Core Java Concurrency”的DZone refcard声明:
设置后,最终字段值 无法改变。将对象引用字段标记为final 不要阻止从该字段引用的对象稍后更改。对于 例如,最终的ArrayList字段不能更改为其他字段 ArrayList,但可以在列表实例上添加或删除对象。
和
最终字段冻结不仅包括对象中的最终字段,还包括所有字段 可从这些最终字段到达的对象。
我对第二个陈述并不完全清楚。这是否意味着如果我在类A的类A中有一个final字段,而后者又有一个Integer类型的final字段,那么A类实例的最终字段冻结仅在{{1}的最终字段冻结后完成已经发生过?
b.c
答案 0 :(得分:11)
这是否意味着如果我有一个决赛 类B类A类中的字段, 而这又有一个最后的领域 输入Integer,然后是最终字段冻结 对于A类的实例完成 只有在最后一场冻结之后 b.c已经发生过?
我想我会谨慎地说,在这种情况下,最终字段冻结意味着当您创建A的实例并且安全地发布时,其他对象将永远不会看到b或c的未初始化值。
我还会说,当你在A中创建B的实例时,A中的其他初始化代码永远不会看到c的未初始化值。
我遇到关于最终字段冻结的实际问题的一个案例是例如一个包含(可变)HashMap的类,仅用于读取,在构造期间初始化:
public class DaysOfWeek {
private final Map daysOfWeek = new HashMap();
public DaysOfWeek() {
// prepopulate my map
daysOfWeek.put(0, "Sunday");
daysOfWeek.put(1, "Monday");
// etc
}
public String getDayName(int dayOfWeek) {
return daysOfWeek(dayOfWeek);
}
}
这里出现的问题是:假设这个对象是安全发布的,并且假设这里没有同步,那么其他线程调用getDayName()是否安全?答案是肯定的,因为最终的字段冻结保证了HashMap和从它可以到达的所有东西(这里只是字符串,但可能是任意复杂的对象)在构造结束时被冻结。 [如果你想在构建之后实际修改这个地图,那么你需要围绕读写进行显式同步。]这里有一个lengthier blog探索这个主题,并检查一些像Brian Goetz这样的人的一些有趣回应的评论。 / p>
btw我是refcard的作者
答案 1 :(得分:6)
Java Concurrency in Practice在第16.3节中提到了这一点:
初始化安全保证 对于正确构造的对象, 所有线程都会看到正确的 已设置的最终字段的值 由构造函数,不管如何 该对象已发布。此外,任何 可以到达的变量 通过正确的最后一个领域 构造对象(如 最终数组的元素或 由a引用的HashMap的内容 最后的领域)也保证 其他线程可见。对象 最后的字段,初始化 安全禁止重新排序任何部分 初始负荷的施工 对该对象的引用。所有 写入由最终字段 构造函数,以及任何 变量可通过这些变量到达 田野,成为“冻结”的时候 构造函数完成,以及任何线程 获得对此的引用 保证对象看到一个值 这至少与最新的一样 冻结价值。写入初始化 变量可以通过final获得 字段不会重新排序 之后的运作 施工后冻结。
答案 2 :(得分:2)
右。这是从JMM
开始的寻找段落:
当构造函数完成时,对象被认为是完全初始化的。在该对象完全初始化之后只能看到对象引用的线程可以保证看到该对象的最终字段的正确初始化值。
由于构造函数在B类初始化B.c
的保证冻结之前不会完成答案 3 :(得分:2)
保证比您想象的要强。最终的字段语义甚至适用于分配给最终字段的可变对象(具有通常的限制)。因此,扩展您的示例以使A.b
私有和B
可变(但不是外部可变的)。
public class A {
private final B b = new B();
public Integer get() { return b.c; }
}
public class B {
public Integer c = 10;
}
在这种情况下,即使在不安全的发布下,A.get
也永远不会返回null
。当然这个例子是完全抽象的,因此毫无意义。通常,它对于数组(例如在String
)和集合中很重要。
答案 4 :(得分:1)
在其他事情发生之前谈论什么是最终的,真的没有意义。对于您的程序,一旦创建了对象(实际上从分配字段的那一刻起),引用就不能再改变了。 由于B实例是在A实例之前创建的,你可以说c在b之前变为final,但它并不重要。
订单的重要性在于您在单个班级中有多个最终字段。如果要在另一个字段的赋值中使用一个final字段的值,则只应访问已经初始化的字段。
老实说,“最终现场冻结”一句对我来说没有多大意义。