javamultithreadingconstructorthread-safetysafe-publication

How private constructors achieve publication safety in Java


In the classic "Java concurrency in Practice" Brian Goetz uses the following snippet of code to demonstrate how to safely publish an object using a private constructor and a factory method:

public class SafeListener {
    private final EventListener listener;

    private SafeListener() {
        listener = new EventListener() {
            public void onEvent(Event e) {
                doSomething(e);
            }
        };
    }

    public static SafeListener newInstance(EventSource source) {
        SafeListener safe = new SafeListener();
        source.registerListener(safe.listener);
        return safe;
    }
}

What I can't figure out yet is how this code achieves safe publication through a private constructor.

I am aware that a private constructor is used to prevent instantiation outside of the object but how does that apply to a thread rather than an object? A thread is not necessarily an object and I can't see what stops another thread from acquiring a reference to safe before the constructor finishes execution.


Solution

  • The constructor’s property of being private has nothing to do with the thread-safety. This is an example of the final field publication guaranty. In order for it to work, the this instance of the final field must not escape during the constructor, therefore the factory method takes care of constructing the holder instance first and registering the listener afterwards. It’s natural for applications of the factory pattern to have private constructors and public factory methods but that is not important to achieve the thread-safety here.

    It’s all about how JIT and the HotSpot optimizer treat the code when performing the optimizations. They know what final or volatile fields are and what a constructor is. They will obey the limitations to the degree of optimization of such constructs. Most important, they ensure that all writes to an object you store in the final field and the write to the final field itself happen-before the constructor ends so that the final field stores happen-before any effect of the registerListener invocation in your example. Therefore, other threads can’t see the listener reference before its correct initialization.

    Note that this is something you should rarely rely on. In your example, if the method registerListener of the EventSource is not thread-safe, still very bad things can happen. On the other hand, if it’s thread-safe, its thread-safety will apply to the listener constructed before the registration as well so the final field guaranty would not be needed.