Bookmark and Share

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.

Last modified: 2010-03-29 08:56 +0300


blog comments powered by Disqus