Java’s final fields are not as final as you may think
Of all my adventures at
stackoverflow.com, one of the most
enlightening ones happened just
recently. Thanks
to Jon Skeet and others, I finally got convinced to favor static
factory methods over constructors. But not for any of the reasons
listed at Effective
Java’s Item #1 Consider
static factory methods instead of constructors — although there
definitely are good points there. What ultimately got me convinced
was, curiously enough, thread safety.
In a nutshell: multiple threads, non-private constructors, final
fields and escaping this reference are a recipe for a disaster.
Sounds crazy, huh? Constructors execute in the thread that calls
them, right? Right. And final fields are particularly thread safe,
right? Almost right.
This is particularly insidious, since all common wisdom says that at
least final fields, being immutable, are thread friendly, needing no
synchronization; stale values do not exist because the fields do not
change so there’s nothing to be stale about. But it’s exactly this
false feel of security that causes problems here. You wouldn’t expect
final fields to mutate. But actually they do mutate! Namely, from
the viewpoint of other threads, between the point where they’re set,
and the point where the constructor returns. There’s a sneaky rule in
the Java Memory Model spec about final fields: they are properly
visible to all threads only after the constructor has returned. Java
Memory Model’s Final Field
Semantics:
An object is considered to be completely initialized when its constructor finishes. A thread that can only see a reference to an object after that object has been completely initialized is guaranteed to see the correctly initialized values for that object’s final fields.
The usage model for final fields is a simple one. Set the final fields for an object in that object’s constructor. Do not write a reference to the object being constructed in a place where another thread can see it before the object’s constructor is finished.
This doesn’t sound too bad alone, but combined with a this escape
the recipe for quite weird, very-hard-to-debug, one-in-a-million
malfunction is ready.
Don’t let this reference escape from the constructor
This is from the item 3.2.1 of Java Concurrency in Practice:
…publishing an object from within its constructor can publish an incompletely constructed object. This is true even if the publication is the last statement in the constructor.
Final fields are what they’re supposed to be only after the constructor has returned. Period. Prior to that, threads (other than the one in the constructor) can see stale values for those fields—most likely their default values (zeros, nulls).
The problem starts to escalate if the this reference escapes from
the constructor (you shouldn’t do that, but hands up who’s not
guilty). The leak can be happen quite subtly—thanks to Java’s
implicit this references, you don’t need to write “this”
explicitly anywhere to leak this. Let’s register an object to
receive particular kinds of events:
public class NumberPrinter {
public NumberPrinter(int number, EventSource source) {
this.number = number; // Initialize the final field
source.registerListener(
new EventListener() {
public void onEvent() {
printNumber(); // Refers to the surrounding instance!
}
});
// Now the registration is done and EventSource source could
// call printNumber(). But the constructor is not yet finished!
}
public void printNumber() {
System.out.println(number);
}
final int number;
}
Here we register the listener in the end of the constructor, and we
have initialized all the relevant fields (just one field in this
case), so that even if the listener causes a call to printNumber()
immediately, before the constructor returns, the object should be in
an appropriate state, ready to receive the call. But, as written
above, final fields are valid only after the constructor has
returned, not before! The code above is slightly broken; it will
probably seem to work ok, but it will fail every now and then.
Note that even if the last statement in the constructor has been executed, it doesn’t guarantee that the constructor itself is finished! The runtime is doing its own voodoo (memory barriers) before the constructor actually returns to its caller.
If source happened to fire very promptly after the printNumber()
call was registered to it—but before the constructor has
returned—then the calling thread is not guaranteed to see the final
int number field in the state it was set to in the constructor just
before registering the listener. The integer can instead be in its
default value (0), and so the “impossible” happens: the object gets
called in a state where its final fields aren’t the values that they
were set to be just a moment ago.
What makes this even funnier is that you could get away with leaking
this references if you were not using final fields, because you
wouldn’t make assumptions of non-final fields’ thread safety
anyway—you would synchronize access to them if the object is to be
called from other threads—and so everything would work properly.
Granted, the real-world likelihood of an event coming in before the constructor has returned may be quite low. But actually it makes the problem far more serious! Unit tests don’t find this kind of bugs. How do you debug something that manifests itself once a week, perhaps only on some particular platform? A nightmare.
Another problem would manifest itself when subclassing abstract
classes or overriding methods, and the child class’s constructor invokes
the super() constructor. The child class must invoke super()
before initializing its own extended state, and so the parent class’s
constructor could call (or register a listener that calls) some of the
overridden methods (that are now implemented in the child class)
before the state they require has been set up! Be careful with
constructors.
Use static factory methods
The only solid way to construct and initialize an object is to use a static factory method:
public class NumberPrinter {
public static newInstance(int number, EventSource source) {
NumberPrinter printer = new NumberPrinter(number);
// The constructor has returned, we can trust on final fields.
source.registerListener(
new EventListener() {
public void onEvent() {
printer.printNumber();
}
});
}
private NumberPrinter(int number) {
this.number = number;
}
public void printNumber() {
System.out.println(number);
}
final int number;
}
So from now on, any time I need to do some work in the constructor,
I’ll make the constructor simple and private, and do the work in a
static factory method instead. Besides being thread safe, it
doensn’t make the object any harder to initialize—just write
MyClass.newInstance() instead of new MyClass()—and it offers
the plethora of other benefits explained in Effective
Java. I’m enlightened.
blog comments powered by Disqus
