Starting a thread as the last statement of a final class constructor
I understand that it is generally a good idea to start a new thread in the constructor , because it can allow this to be avoided before it is fully built. For example:
public final class Test {
private final int value;
public Test(int value) throws InterruptedException {
start();
this.value = value;
}
private void start() throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("Construction OK = " + Boolean.toString(Test.this.value == 5));
}
}).start();
}
}
public static void main(String[] args) throws InterruptedException {
Test test = new Test(5);
}
}
This prints (obviously not every time):
Design OK = false
Design OK = false
Design OK = false
Design OK = false
Design OK = false
Design OK = false
Design OK = false
Design OK = true
Design OK = true
Design OK = true
Now the IF method start
is the last AND operator of the constructor , in which the lock locks around the final initialization of the value, is there still a risk associated with starting threads from the constructor?
public Test(int value) throws InterruptedException {
synchronized (new Object()) { // to prevent reordering + no deadlock risk
this.value = value;
}
start();
}
EDIT
I don't think this has been asked before, in the sense that the question is more specific than just "Can I start threads in the constructor": threads are started at the last assertion of the constructor, which means that the object's construction is complete (as I understand it ).
source to share
In this particular case, I would consider marking value
as volatile
(or use AtomicBoolean
) and start streams after the value is set:
this.value = value; // this.value.set(value) if using AtomicBoolean
start();
If you are going to use this little dodgy solution, I would make a class final
to avoid the problem described by Andreas_D.
Regarding your edit:
[...] which means that the construction of the object is complete (as far as I understand).
This is correct, but consider the following scenario:
Your test streams are a little more complex and refer to a list of tests testList
. Now if you do
testList.add(new Test());
a thread running in a constructor may not find a related test in the list because it has not been added yet. This can be avoided by doing instead
Test t = new Test();
testList.add(t);
t.start();
Related question:
source to share
In the constructor, you call the start method with
start()
class. Now you can notice that the method you are calling is an object of this class that has not yet been created. This way you are still passing the non-constructed object reference to the method. You have included method directly in object creation, whereas any method on an object must be called after the object is fully constructed.
So there is a risk associated with this. It was also a very good question.
source to share
synchronized(new Object())
Does NOT prevent reordering - since the monitor is a local variable, the compiler can actually ignore the synchronized block .
In particular, the compiler can prove that it is impossible for two locks to lock on the same monitor (by the definition of local variables), and therefore a synchronized block is redundant and can be ignored.
source to share