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.
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.