线程中final字段冻结语义疑问:构造器修改后hb关系与f.y可见性
关于final字段与happens-before顺序的问题解答
先看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 } } }
当把构造器修改为以下形式后:
public FinalFieldExample() { y = 4; x = 3; }
我们来分析两个问题:
1. hb(y=4, f=new FinalFieldExample()) 是否成立?
成立。原因如下:
- 同一线程内的程序顺序决定了
hb(y=4, x=3)——先执行的操作happens-before后执行的操作。 - 根据JLS的final字段冻结规则,
hb(x=3, f=new FinalFieldExample())——final字段的赋值动作happens-before对象引用被发布(即赋值给f)的动作。 - happens-before关系具备传递性:如果
hb(A,B)且hb(B,C),则hb(A,C)。由此可推导出hb(y=4, f=new FinalFieldExample())成立。
2. reader()中的f.y是否能保证看到4?
不能保证。
虽然writer线程内部存在hb(y=4, f=new FinalFieldExample())的关系,但这仅约束writer线程内的操作顺序,无法保证reader线程能看到writer线程中y的赋值结果:
f是普通静态变量,writer线程对f的赋值和reader线程对f的读取之间没有happens-before关系。- 即便reader线程看到
f != null,也可能因JVM对普通字段读写的重排序、缓存一致性问题,读取到y的初始值0。 - 只有final字段的赋值会因冻结规则获得跨线程的可见性保证,普通字段没有这种特殊待遇。
内容的提问来源于stack exchange,提问作者hao zhang
相关产品推荐
相关产品推荐

