java - 为什么代码会面临看到部分构造的对象的风险?

标签 java multithreading concurrency volatile

有一个article关于 ibm 中 volatile 的使用,其解释让我很困惑,下面是本文中的一个示例及其解释:

public class BackgroundFloobleLoader {
    public volatile Flooble theFlooble;

    public void initInBackground() {
        // do lots of stuff
        theFlooble = new Flooble();  // this is the only write to theFlooble
    }
}

public class SomeOtherClass {
    public void doWork() {
        while (true) { 
            // do some stuff...
            // use the Flooble, but only if it is ready
            if (floobleLoader.theFlooble != null) 
                doSomething(floobleLoader.theFlooble);
        }
    }
}

Without the theFlooble reference being volatile, the code in doWork() would be at risk for seeing a partially constructed Flooble as it dereferences the theFlooble reference.

如何理解这一点?为什么没有 volatile,我们可以使用部分构造的 Flooble 对象?谢谢!

最佳答案

没有volatile你可以看到一个部分构造的对象。例如。考虑一下Flooble对象。

public class Flooble {
    public int x;
    public int y;

    public Flooble() {
       x = 5;
       y = 1;
    }
}

public class SomeOtherClass {
    public void doWork() {
    while (true) { 
        // do some stuff...
        // use the Flooble, but only if it is ready
        if (floobleLoader.theFlooble != null) 
            doSomething(floobleLoader.theFlooble);
    }

    public void doSomething(Flooble flooble) {
        System.out.println(flooble.x / flooble.y);
    }
}

}

没有volatile doSomething 方法不能保证看到值 51对于 xy 。例如,它可以看到 x == 5但是y == 0 ,导致除以零。

当您执行此操作时theFlooble = new Flooble() ,发生了 3 次写入:

  1. tmpFlooble.x = 5
  2. tmpFlooble.y = 1
  3. theFlooble = tmpFlooble

如果这些写入按此顺序发生,则一切正常。但没有 volatile编译器可以自由地重新排序这些写入并按照自己的意愿执行它们。例如。首先是第 3 点,然后是第 1 点和第 2 点。

这种情况实际上一直在发生。编译器确实会对写入进行重新排序。这样做是为了提高性能。

该错误很容易通过以下方式发生:

主题A执行initInBackground()类中的方法 BackgroundFloobleLoader 。编译器会在执行 Flooble() 的主体之前对写入进行重新排序。 (其中设置 xy),线程 A首先执行theFlooble = new Flooble() 。现在,theFlooble指向一个flooble实例,其xy0 。线程之前A继续,其他一些线程 B执行方法doWork()SomeOtherClass 。该方法调用方法doSomething(floobleLoader.theFlooble)当前值为theFlooble 。在这个方法中theFlooble.x除以 theFlooble.y导致被零除。主题B由于未捕获的异常而完成。主题A继续并设置theFlooble.x = 5theFlooble.y = 1 .

这种情况当然不会在每次运行时都会发生,但根据 Java 的规则,可能会发生。

关于java - 为什么代码会面临看到部分构造的对象的风险?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34757928/

相关文章:

整个应用程序的 JavaFx css

asp.net - 无法配置IIS/Asp.NET来同时处理许多异步请求

java - 为什么 Java 并发处理不能处理新实例化的对象,而可以处理同一类的反序列化对象?

java - 启动画面中的 GIF 不流畅

带有 SHAwithECDSA 的 Java Signature.sign() 在多次运行时产生不同的结果

python - Python 线程中的死锁

c# - 检查 Dispatcher.CurrentDispatcher.Thread.ManagedThreadId 后的 CollectionView NotSupportedException

python - 在Python中执行http请求最快的方法是什么

c# - Java 自适应线程池的设计注意事项

java - 更改图像样式会导致触发 LoadEvent 吗?