为了账号安全,请及时绑定邮箱和手机立即绑定

理解为什么在Java内存模型方面在构造函数内启动线程是不安全的[重复]

理解为什么在Java内存模型方面在构造函数内启动线程是不安全的[重复]

12345678_0001 2019-04-13 10:11:55
根据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


查看完整回答
反对 回复 2019-05-15
?
慕的地10843

TA贡献1785条经验 获得超8个赞

原因是在完全构造对象之前,这会将this指针暴露给另一个线程。

在您的代码中,对象已经构造得足够value已设置为10),因此它将按预期运行。


查看完整回答
反对 回复 2019-05-15
  • 2 回答
  • 0 关注
  • 685 浏览

添加回答

举报

0/150
提交
取消
微信客服

购课补贴
联系客服咨询优惠详情

帮助反馈 APP下载

慕课网APP
您的移动学习伙伴

公众号

扫描二维码
关注慕课网微信公众号