Is it safe to let "this" escape in the last statement of a constructor if it has happens-before guarantees?

Viewed 79

A common advice in Java is to not to let the "this" reference escape during construction of an object and therefore not start any threads in a constructor. But I find myself writing a lot of classes that should start a thread using an executor. According to the common advice, I should write an extra start() method that submits the task to the executor.

But submitting a task to an executor gives happens-before guarantees as documented here. So would it be fine to submit the task in the last statement of the constructor? Or the more general question: is it safe to let "this" escape in the last statement of a constructor if that statement provides happens-before guarantees?

2 Answers

Yes, this is safe, because the statement providing happens-before guarantees will make sure all fields are correctly initialized visible to other threads. One caveat is that a subclass could ruin this safety so its better to make the class final. But, as Holger pointed out, even then an additional constructor delegating to the one that started the thread could harm safety.

The general advice "don't let this escape from the constructor" exists mainly because it is easier and thus less error prone to follow this rule then to keep all nuances in mind (like subclassing).

The Answer by Stefan Feuerhahn is correct.

I’ll add the suggestion that embedding an executor service within the class performing the work can be a “code smell”, an indication of weak design.

Generally we want to follow the single responsibility principle in our designs. A class should have a single purpose, and should try not to stray from that narrow specific purpose.

If, for example, a class were written to create a report, that class should know only about that report. That class should not know about when that report should be run, or how often to run the report, or what other code cares about if the report has been run.

Such scheduling of when to run the report is tied to the lifecycle of the app. For one important thing, the executor service must eventually be shut down when no longer needed or when the app is exiting. Otherwise the backing thread pool may continue indefinitely like a zombie . Your report-generating class should not know about when it is no longer needed, nor should it know about when or why the app is exiting.

Another aspect of the issue is that configuring an executor service involves knowing about the deployment scenario. How much RAM, how many CPU cores, how much other burden on that host machine, all contribute to decisions about how to set up the executor service(s). Your report-generating code should not have to change because of changes to your deployment situation.

The report-generating class should not know anything about the calling app’s lifecycle, not know anything about the executor service. The report-generating app should know nothing more than how to generate that one report. Some other place in your code, perhaps some report manager class or your app’s lifecycle orchestration code, should handle how often and when to run the report.

Related