使用setAccessible进行最终字段语义和反序列化(true)

Bru*_*eis 9 java final thread-safety deserialization

根据Java内存模型,final在对象的构造函数中初始化的字段不受进一步修改,保证每个读取它的线程都能正确看到它的值,即使对象本身已经与数据竞争一起发布.

JLS讨论了17.5.3最终字段的后续修改,并含糊地说明了这一点

实现可以提供在最终字段安全上下文中执行代码块的方式.

它似乎并没有真正定义这些修改的语义,也不确定这个最终的字段安全上下文必须存在的位置或如何定义它(即,JLS似乎并未对最终字段的后续修改提供任何保证).

我必须说我没有完全理解部分命令dereferences()mc(),也没有完全理解在对最终字段进行任何修改之后发生的冻结操作的行为(归因于它的初始值或后续修改).

在这种情况下,我想知道的是:(de)序列化框架(如Gson)如何保证包含在构造函数中正确初始化的最终字段的反序列化对象不会造成线程可见性问题?

例如,考虑这个类:

class X {
  private final String s;
  public X(final String s) { this.s = s; }
  @Override public String toString() { return s; }
}
Run Code Online (Sandbox Code Playgroud)

以下代码:

final Gson gson = new Gson();
X x = gson.fromJson(gson.toJson(new X("abc")), X.class);
System.out.println(x);
// prints abc
Run Code Online (Sandbox Code Playgroud)

fromJson使用调试器进入该方法,我看到它sun.misc.Unsafe用于分配一个实例X而不调用它的构造函数,并且字段是setAccessible(true),最后它们被设置.

这只是在Sun的(或兼容的)JVM中!看起来Gson也有特定于多个Android版本的代码.

那么,这些反序列化的最终字段是否有任何线程安全保证,就像我对X构造的实例一样new X("abc")?如果是这样,这种担保来自哪里?

谢谢!

irr*_*ble 0

来自JLS

Final 字段的冻结发生在设置 Final 字段的构造函数的末尾,以及每次通过反射或其他特殊机制修改 Final 字段之后。

由于记忆效应仅根据操作来定义freeze,这意味着如果在final构造函数之后修改字段,语义也适用 - 只要在此之前没有泄漏对象引用。这被认为是合法的用例。

一旦对象引用被发布,进一步修改 Final 字段就不是一个好主意。