根据 Java 内存模型,一个 final
在对象的构造函数中初始化的字段不受进一步修改的影响,保证每个读取它的线程都能正确看到它的值,即使对象本身已经发布了数据竞争。
JLS 谈论17.5.3 Subsequent Modification of Final Fields , 并含糊地说
An implementation may provide a way to execute a block of code in a final field safe context.
它似乎并没有真正定义这种修改的语义,也没有确切地定义这个 final 字段安全上下文 东西必须存在的地方或如何定义它(即,JLS 似乎没有给出对最终字段的后续修改的任何保证)。
我必须说,我没有完全理解部分命令 dereferences() 和 mc(),也没有完全理解 freeze 的行为在对最终字段进行任何修改(归因于它的初始值或后续修改)之后发生的操作。
在这种情况下,我想知道的是:(反)序列化框架(例如 Gson)如何保证包含在构造函数中正确初始化的最终字段的反序列化对象不会造成线程可见性问题?强>
例如,考虑这个类:
class X {
private final String s;
public X(final String s) { this.s = s; }
@Override public String toString() { return s; }
}
以及以下代码:
final Gson gson = new Gson();
X x = gson.fromJson(gson.toJson(new X("abc")), X.class);
System.out.println(x);
// prints abc
进入方法fromJson
使用调试器,我看到 sun.misc.Unsafe
用于分配 X
的实例无需调用其构造函数,字段为 setAccessible(true)
, 最后他们设置好了。
而且这仅在 Sun 的(或兼容的)JVM 中!看起来 Gson 也有针对多个 Android 版本的代码。
那么,是否有与这些反序列化的最终字段相关的任何线程安全保证,就像我对 X
的实例所做的那样用 new X("abc")
构建?如果是,此保证从何而来?
谢谢!
最佳答案
线程安全
正如我所读,线程安全保证来自于给定属性被声明为最终属性这一事实。它不是线程安全的点是:
- 在反序列化过程中,分配对象内存空间时,但在为
final
属性赋值之前 - 期间通过反射 API 修改
final
字段(即,当值处于修改过程中,并且在此过程完成之前)
这里需要注意的是,您链接到的引用允许存在一些其他而不是反射 API(但具有相同的final
字段修改能力)的理论可能性.
卡住
Freezes of a final field occur both at the end of the constructor in which the final field is set, and immediately after each modification of a final field via reflection or other special mechanism.
就卡住调用而言,基本上是说 freeze
用于将属性标记为“无法更改”,它确实这样做了:
- 在构造函数执行结束时,未定义的
final
字段实际被赋值 - 在
final
字段值更改后,通过 Reflection API 之类的东西
线程安全问题仅适用于被修改/反序列化的对象。
所以:
(...code that comes before...)
END final field safe context - entering non-threadsafe area
final fields are not frozen
code that deserializes an object OR modifies a final field via reflection
final fields are re-frozen
RESUME final field safe context - re-entering threadsafe area
(...code that comes after...)
因此……
在构造函数运行后(即你有一个分配了值的实例化对象),final 字段不能被改变,因为它被卡住,除非使用反射 API 直接改变该值 - 之后它再次被卡住,因为毕竟该字段被声明为最终字段。
如果您正在寻找特定于 Android 的答案(以确保其 JVM 行为与 Sun/Oracle 的 JVM 一致),您需要找到该 JVM 的等效文档。
关于java - 使用 setAccessible(true) 的最终字段语义和反序列化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7654747/