Final field semantics & deserialization with setAccessible(true)-Collection of common programming errors
According to the Java Memory Model, a final
field initialized in the object’s constructor not subject to further modifications is guaranteed to have its value correctly seen by every thread reading it, even if the object itself has been published with data races.
The JLS talks about 17.5.3 Subsequent Modification of Final Fields, and vaguely states that
An implementation may provide a way to execute a block of code in a final field safe context.
It does not seem to really define the semantics of such modifications, nor where exactly this final field safe context thing must exist or how to define it (ie, the JLS doesn’t seem to give any guarantee about subsequent modification of final fields).
I must say that I did not fully understood the partial orders dereferences() and mc(), nor the behavior of the freeze action that takes place after any modification to a final field (either the initial value attributed to it or subsequent modifications).
On this context, what I want to know is: how can a (de)serialization framework, such as Gson, guarantee that deserialized objects containing final fields properly initialized in a constructor will pose no thread visibility problem?
For example, consider this class:
class X {
private final String s;
public X(final String s) { this.s = s; }
@Override public String toString() { return s; }
}
And the following code:
final Gson gson = new Gson();
X x = gson.fromJson(gson.toJson(new X("abc")), X.class);
System.out.println(x);
// prints abc
Stepping into the method fromJson
with a debugger, I see that sun.misc.Unsafe
is used to allocate an instance of X
without calling its constructor, and the fields are setAccessible(true)
, and finally they get set.
And this is only in Sun’s (or compatible) JVMs! It looks like Gson has code specific to multiple Android versions as well.
So, are there any thread-safety guarantees associated with these deserialized final fields, just like I would have with an instance of X
constructed with new X("abc")
? If so, where does this guarantee come from?
Thanks!