I'm reading a book "Java concurrency in practice" by Brian Goetz. Paragraphs 3.5 and 3.5.1 contains statements that I can not understand.
Consider the following code:
public class Holder {
private int value;
public Holder(int value) {
this.value = value;
}
public void assertValue() {
if (value != value) throw new AssertionError("Magic");
}
}
class HolderContainer {
// Unsafe publication
public Holder holder;
public void init() {
holder = new Holder(42);
}
}
Author states that:
- In Java, Object constructor first writes default values to all fields before subclass constructor run.
- Therefore it's possible to see field default value as a stale value.
- Thread may see stale value the first time it reads a field and then a more up-to-date value the next time, which is why assertN can throw AssertionError.
So, according to the text, with some unlucky timing it is possible that value = 0; and in the next moment value = 42.
I agree with point 1 that Object constructor firstly fills fields with default values. But I don't understand points 2 & 3.
Let's update authors code and consider the following example:
public class Holder {
int value;
public Holder(int value) {
//Sleep to prevent constructor to finish too early
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
this.value = value;
}
public void assertValue() {
if(value != value) System.out.println("Magic");
}
}
I've added Thread.sleep(3000), to force thread to wait before object will be fully constructed.
public class Tests {
private HolderContainer hc = new HolderContainer();
class Initialization implements Runnable {
public void run() {
hc.init();
}
}
class Checking implements Runnable {
public void run() {
hc.holder.assertValue();
}
}
public void run() {
new Thread(new Initialization()).start();
new Thread(new Checking()).start();
}
}
In example:
- first thread inits holder object
- second thread calls assertValue
Main Thread runs two threads:
- new Thread(new Initialization()).start(); It tooks 3 seconds to fully construct Holder object
- new Thread(new Checking()).start(); since Holder object still not constructed code will throw an NullPointerException
Therefore, it's impossible to emulate situation when field has default value.
My Questions:
- Author was wrong about this concurrency problem?
- Or It it impossible to emulate behaviour for fields default values?
Tests
class from the book ? (sorry, skipped those two point) – Biteif (value != 42)
. – Klumpvalue != value
check so that it's always false. Nonetheless, the author is correct. Many concurrency bugs only show up when you're unlucky. Also, @Klump is not correct when he says the problem is only possible if the thread is stared in the construtor. Other threads may not see writes in the same order that they are made. – Transposition