问题
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?
回答1:
You are probably trying to simulate a concurrency scenario, which I believe is very hard to simulate using a couple of threads.
The following test-case which you have written is not correct at all is more likely to throw a NullPointerException.
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();
}
}
What if your Checking Thread executes before Initialization one?? Also putting a sleep there simply means that executing thread will sleep and does tell you about the internal atomic operations being performed by then.
回答2:
Did not reproduce it with your code. Here is an example to emulate un-safe publication. The strategy is let one thread publication Holder
and let another check its value.
class Holder {
private volatile int value;
public Holder(int value, HolderContainer container) {
container.holder = this; // publication this object when it is not initilized properly
try {
Thread.sleep(10);
} catch (Exception e) {
}
this.value = value; // set value
}
public int getValue() {
return value;
}
}
class HolderContainer {
public Holder holder;
public Holder getHolder() {
if (holder == null) {
holder = new Holder(42, this);
}
return holder;
}
}
public class Tests {
public static void main(String[] args) {
for (int loop = 0; loop < 1000; loop++) {
HolderContainer holderContainer = new HolderContainer();
new Thread(() -> holderContainer.getHolder()).start();
new Thread(() -> {
Holder holder = holderContainer.getHolder();
int value1 = holder.getValue(); // might get default value
try {
Thread.sleep(10);
} catch (Exception e) {
}
int value2 = holder.getValue(); // might get custom value
if (value1 != value2) {
System.out.println(value1 + "--->" + value2);
}
}).start();
}
}
}
来源:https://stackoverflow.com/questions/50655856/concurrency-object-creation-in-java