final 字段为什么在对象构造完成后有特殊的可见性保证?什么情况下保证会失效?这篇先用确定性的实验验证 this 逃逸,再看反射对 final 的影响,最后用 jcstress 在数据竞争发布下比较 final 与普通字段——并如实说明这个比较在本机没能区分两者(上一篇的内存序同理)。
构造器还没结束时另一个线程读到:fin = 0,plain = 0(this 逃逸:final 字段也是默认值 0) 构造完成之后:fin = 42,plain = 42
编译期常量 final int inst = 1:setAccessible(true) 之后 f.setInt(b, 99) 成功,反射读 = 99,直接 b.inst = 1(javac 把常量 1 内联进了读取处,反射改的值读不到) 构造器里赋值的 final int computed = 5:f.setInt(b, 99) 成功,反射读 = 99,直接 b.computed = 99,b.readComputed() = 99 热循环(200 万次,已 JIT)里读 computed 求和 = 198000000(每次都读到 99 应是 198000000;读到旧值 5 则是 10000000) static final 字段 STATIC:f.setInt 抛 IllegalAccessException: Can not set static final int field Main$Box.STATIC to (int)5 record 的字段 v:f.setInt 抛 IllegalAccessException: Can not set final int field Main$Rec.v to (int)5
[a、b 都是 final] 状态 OK;(-1, -1) 50,298,295 (0, 0) 0 (0, 1) 0 (1, 0) 0 (1, 1) 612,956 [a 是 final,b 是普通字段] 状态 OK;(-1, -1) 97,033,103 (0, 0) 0 (0, 1) 0 (1, 0) 0 (1, 1) 30,534,788 [a、b 都是普通字段] 状态 OK;(-1, -1) 45,508,797 (0, 0) 0 (0, 1) 0 (1, 0) 0 (1, 1) 599,894