根据Java Concurrency in Practice,在类构造函数中启动一个线程是危险的。原因是this在完全构造对象之前,这会将指针暴露给另一个线程。尽管在之前的许多StackOverflow问题中都讨论过这个主题,但我仍然难以理解为什么会出现这种问题。让我举一个具体的例子说明我想做的事情。(此段代码的所需输出是将数字20打印到控制台。)private static class ValueHolder {
private int value;
private Thread thread;
ValueHolder() {
this.value = 10;
thread = new Thread(new DoublingTask(this)); // exposing "this" pointer!!!
thread.start(); // starting thread inside constructor!!!
}
int getValue() {
return value;
}
void awaitTermination() {
try {
thread.join();
} catch (InterruptedException ex) {}
}}private static class DoublingTask implements Runnable {
private ValueHolder valueHolder;
DoublingTask(ValueHolder valueHolder) {
this.valueHolder = valueHolder;
}
public void run() {
System.out.println(Thread.currentThread().getName());
System.out.println(valueHolder.getValue() * 2); // I expect this to print out 20...
}}public static void main(String[] args) {
ValueHolder myValueHolder = new ValueHolder();
myValueHolder.awaitTermination();}是的,我知道线程是在我们从构造函数返回之前启动的。是的,我知道this指针暴露给线程。然而,我确信代码是正确的 - 我相信它总会将数字20打印到控制台。任务this.value = 10 发生在之前 thread.start()。(这是因为在执行顺序this.value = 10之前thread.start()。)thread.start()主线程中的调用发生.run()在新创建的线程中方法开始之前。在开始.run()的方法之前发生的System.out.println(valueHolder.getValue() * 2);print语句。(再次,按执行顺序。)因此,通过Java Memory Model,print语句应该读取正确初始化的值valueHolder.value(即10)。尽管忽略了Java Concurrency in Practice的建议,但我的代码应该可以正常工作。我弄错了吗?我错过了什么?
2 回答

手掌心
TA贡献1942条经验 获得超3个赞
假设我是你的类的子类。它可能没有在需要时初始化其字段。
class BetterValueHolder extends ValueHolder{ private int betterValue; BetterValueHolder(final int betterValue) { // not actually required, it's added implicitly anyway. // just to demonstrate where your constructor is called from super(); try { Thread.sleep(1000); // just to demonstrate the race condition more clearly } catch (InterruptedException e) {} this.betterValue = betterValue; } @Override public int getValue() { return betterValue; }}
这将打印为零,无论给予构造函数的值是什么BetterValueHolder
。
添加回答
举报
0/150
提交
取消