在多线程环境中读取的值

Roh*_*hit 7 java multithreading

我正在浏览有关Thread和Locks的JLS文档http://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.5.

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
    } 
} 
}
Run Code Online (Sandbox Code Playgroud)

我对上面提到的关于fy如何被视为零的例子(前17.5-1)感到困惑.Reader Threads将对象f读取为null,在这种情况下它不会执行任何操作,或者它将通过一些引用读取对象f.如果对象f有引用,则构造函数必须已完成其执行,即使多个Writer线程正在运行,以便可以将引用分配给f,如果构造函数已执行,则应将fy视为4.

在什么条件下可以fy = 0?

谢谢

Gra*_*ray 5

在什么条件下可以fy = 0?

Java内存模型允许JIT编译器重新排序构造函数外部非最终字段的初始化.该字段是最终的,因此必须由JVM初始化,但不是最终的.因此,有可能分配和设置,但字段的初始化尚未完成.xyFinalFieldExamplestatic FinalFieldExample fy

引用17.5-1:

因为writer方法在对象的构造函数完成后写入f,所以读者方法将保证看到fx的正确初始化值:它将读取值3.但是,fy不是final; 因此,读者方法不能保证看到它的值4.

因为f.y不是最终的,所以不能保证在构造函数完成和static f分配时已经设置了它.所以有一个竞争条件被创建,reader可能会看到y3或0取决于这个种族.

  • 或许在这里需要注意术语.对于大多数人来说:'编译器'的意思是`javac`,编译器不会重新排序任何东西.热点,JIT编译器,可以重新排序指令.但即便如此,"编译器"可能甚至不是整个故事,因为硬件(CPU)也可以执行指令重新排序.缓存行为也可能给出指令重新排序的外观. (2认同)