最近,当我提到JMM提供与"final“相关的保证时,我感到很困惑。以下是JMM的摘录和示例
图4给出了一个示例,演示了finalfi屏蔽与普通fi屏蔽的比较。类FinalFieldExample具有一个final intfield x和一个非final intfield y,一个线程可以执行方法编写器(),另一个线程可以执行方法读取器()。因为编写器()在对象的构造函数finishes之后写入f,所以阅读器()将保证看到F.X的正确初始化值:它将读取值3。然而,f.y不是final;因此,reader()方法不能保证看到它的值4。
class FinalFieldExample {
final int x;
int y;
static FinalFieldExample f;
public FinalFieldExample() {
x = 3;
y = 4;
}
static void writer() {
f = new FinalFieldExample();
}
static void reader() {
if (f != null) {
int i = f.x; // guaranteed to see 3
int j = f.y; // could see 0
}
}
}我的困惑是,对象'Obj‘有最终字段和非最终字段是完全初始化的,并且被线程'T’引用,T只会看到最终字段的正确值?建设后不会发生变异的非最终字段怎么办?我知道,如果它们在构造后发生变异,线程'T‘可能看不到新的值(除非字段是易失性的)。但是,如果这个领域是非最终的,非易变的,并且在建造后不发生变异,我会怎样呢?
JVM如何实现与'final‘相关联的保证?例如,对于易挥发的,有记忆障碍。
发布于 2013-11-13 23:50:32
这一问题在本答复中得到了解决:
引用如下:
问题的核心是优化和重新排序指令。如果有两个线程正在使用未同步的构造对象,则可能会发生以下情况:为了提高效率,编译器决定重新排序指令,并为对象分配内存空间,并在item字段完成构造函数和字段初始化之前将其引用存储在其中。或者,它可以重新排序内存同步,以便其他线程能够这样理解它。
如果将某个字段标记为最终字段,则强制编译器在构造函数完成之前完成该字段的初始化。非最终字段没有这样的保证。
这是Java语言定义(17.4)的一部分。关于final字段的详细信息也在JLS (17.5)中。
更具体地说,writer()方法构造一个FinalFieldExample实例,并将其存储在static字段中,供其他线程使用。由于指令重新排序,y字段可能尚未初始化。如果同一个线程调用reader(),它将将y视为4,但其他线程可能会将其视为0,因为f可能是在初始化和发布y之前设置和使用的。
要使代码正确,您必须使f也成为volatile。
发布于 2013-11-14 02:27:32
JVM如何实现与'final‘相关联的保证?例如,对于易挥发的,有记忆障碍。
为了遵守final字段的语义,不能进行一些重新排序,可能需要一些内存屏障(在某些处理器上)。请参阅http://g.oswego.edu/dl/jmm/cookbook.html
这意味着final不是免费午餐。在我们在任何地方使用final之前都要考虑到这一点。(仅仅因为它在约书亚的书中并不意味着它是正确的)
发布于 2013-11-13 23:47:25
我的困惑是,对象'Obj‘有最终字段和非最终字段是完全初始化的,并且被线程'T’引用,T只会看到最终字段的正确值?
保证所有线程都能看到final字段的正确值。这与非最终字段无关。
建设后不会发生变异的非最终字段怎么办?
final修饰符仅适用于特定字段。您可能会对其他非最终字段感到“幸运”,但保证只适用于标记为final的字段。
非最终字段是在构造函数中设置的,而不是稍后修改的,这与此无关。
https://stackoverflow.com/questions/19966662
复制相似问题