对于final域,编译器和处理器遵循两个重排序规则:
- 在构造函数内对一个final域的写入,与随后把这个被构造对象的引用赋值给另一个引用变量,这两个操作之间不能重排序。
- 初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能重排序。
我测试了半天没测出来。。。
public class Test {
static Test test;
int i;
final int j;
public Test(){
i=1;
j=2;
}
static void write(){
test = new Test();
}
static void read(){
// try {
// sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
Test t = test;
System.out.println(t.i);
System.out.println(t.j);
}
public static void main(String[] args) {
Thread t = new Thread(new Runnable() {
@Override
public void run() {
Test.write();
}
});
t.start();
Test.read();
}
}
大概如上代码,注释是在测试的时候打的(但我没有测出来)。
测出来条件:在线程A执行write方法,线程B在执行read()方法的时候
解释一下上述内容,第一条就是对于Test构造方法里边对j的初始化必须在read方法之前。
实现细节:
在对final域写之后,构造函数return之前,会加一个StroreStrore屏障,这个屏障会禁止把final域的写重排序到构造函数之外。 这样写final域的重排序规则可以确保,在对象引用为任意线程可见之前,对象的final已经被正确的初始化了。
那么第二条细节:
这条是关于读final域的规则,在一个线程总,初次读对象引用与初次读该对象包含的final,这两个之间不会重排序这两个操作。
即:在读一个对象的fianl域以前,先回读取final对象的引用。