根据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的建议,但我的代码应该可以正常工作。
我弄错了吗?我错过了什么?
手掌心
慕的地10843
相关分类