1. Overview
In this quick tutorial, we're going to see why we shouldn't start a thread inside a constructor.
First, we'll briefly introduce the publication concept in Java and JVM. Then, we'll see how this concept affects the way we start threads.
2. Publication and Escape
Every time we make an object available to any other code outside of its current scope, we basically publish that object. For instance, publishing happens when we return an object, store it into a public reference, or even pass it to another method.
When we publish an object that we shouldn't have, we say that the object has escaped.
There are many ways that we can let an object reference escape, such as publishing the object before its full construction. As a matter of fact, this is one of the common forms of escape: when the this reference escapes during object construction.
When the this reference escapes during construction, other threads may see that object in an improper and not fully-constructed state. This, in turn, can cause weird thread-safety complications.
3. Escaping with Threads
One of the most common ways of letting the this reference escape is to start a thread in a constructor. To better understand this, let's consider an example:
public class LoggerRunnable implements Runnable { public LoggerRunnable() { Thread thread = new Thread(this); // this escapes thread.start(); } @Override public void run() { System.out.println("Started..."); } }
Here, we explicitly pass the this reference to the Thread constructor. Therefore, the newly started thread might be able to see the enclosing object before its full construction is complete. In concurrent contexts, this may cause subtle bugs.
It's also possible to pass the this reference implicitly:
public class ImplicitEscape { public ImplicitEscape() { Thread t = new Thread() { @Override public void run() { System.out.println("Started..."); } }; t.start(); } }
As shown above, we're creating an anonymous inner class derived from the Thread. Since inner classes maintain a reference to their enclosing class, the this reference again escapes from the constructor.
There's nothing inherently wrong with creating a Thread inside a constructor. However, it's highly discouraged to start it immediately, as most of the time, we end up with an escaped this reference, either explicitly or implicitly.
3.1. Alternatives
Instead of starting a thread inside a constructor, we can declare a dedicated method for this scenario:
public class SafePublication implements Runnable { private final Thread thread; public SafePublication() { thread = new Thread(this); } @Override public void run() { System.out.println("Started..."); } public void start() { thread.start(); } };:
As shown above, we still publish the this reference to the Thread. However, this time, we start the thread after the constructor returns:
SafePublication publication = new SafePublication(); publication.start();
Therefore, the object reference does not escape to another thread before its full construction.
4. Conclusion
In this quick tutorial, after a brief introduction to the safe publication, we saw why we shouldn't start a thread inside a constructor.
More detailed information about the publication and escape in Java can be found in the Java Concurrency in Practice book.
As usual, all the examples are available over on GitHub.