这是是一个我们平时不太注意的一个重点知识。
写final域的重排序规则禁止把 final 域的写重排序到构造方法之外。这个规则的实现是以下两点:
1)JMM 禁止编译器吧 final 域的重写排序到构造函数之外。
就是说构造函数里的写操作,必须要在构造函数内完成。
2)编译器会在 final 域的写之后,在构造方法 return 返回之前,插入一个 StoreStore 屏障。这个屏障禁止处理器把 final 域的写重排序到构造方法之外。
只要对象是正确构造的,(被构造对象的引用在构造方法中没有 “溢出”),那么不需要使用同步(lock或者volatile的使用)就可以保证任意线程都能看到这个 final 域在构造方法中被初始化后的值。
如下面的代码,1和2可能重排序,当两个线程一个writer() 一个 reader()。那么可能的后果就是obj不为null,但是 i 还没没被赋值。所以在构造方法没执行完毕的时候,不要把他暴露出来,以免重排序造成错误的结果。
class Demo{
final int i;
static Demo obj;
public Demo(){
i = 1;// 1 写 final 域
obj = this;// 2 this 引用在这溢出
}
public static void writer(){
new Demo();
}
public static void reader(){
if(obj!=null){//3
int temp = obj.i;//4
}
}
}
网友评论