在这种情况下,是否会抛出此AssertionError?
首先是代码,来自JCIP列出http://jcip.net/listings/StuffIntoPublic.java和http://jcip.net/listings/Holder.java
public class SafePublication { public static void main(String[] args) throws InterruptedException { // System.out.println(Thread.currentThread().getName()); StuffIntoPublic t = new StuffIntoPublic(); t.initialize(); while (true) { new Thread(() -> { t.holder.assertSanity(); }).start(); } } } //@author Brian Goetz and Tim Peierls class StuffIntoPublic { public Holder holder; public void initialize() { // System.out.println(Thread.currentThread().getName()); holder = new Holder(42); } } //@author Brian Goetz and Tim Peierls class Holder { private int n; public Holder(int n ) { this.n = n; } public void assertSanity() { if (n != n) { throw new AssertionError("This statement is false."); } } }
我是说在这种情况下永远不会抛出AssertionError,因为Thread.start()在保证之前发生。 被注释的两个System.out.printlns都打印main,这意味着主线程通过在while(true)循环中的线程上创建和调用start来生成所有后来的线程。
由于这是创建和初始化Holder的线程,所有后续线程都可以安全地成为一个完全可见的持有者,因为之前发生的保证。 我对吗?
我甚至尝试运行这段代码很长时间没有断言错误。
但是,如果主要看起来如下,那么我相信它可能会出现AssertionError
public static void main(String[] args) throws InterruptedException { System.out.println(Thread.currentThread().getName()); StuffIntoPublic t = new StuffIntoPublic(); new Thread(() -> t.initialize() ).start(); while (true) { new Thread(() -> { t.holder.assertSanity(); }).start(); } }
是的,这是安全的,因为Thread#start
保证发生在之前 。 更加冗长:在Thread#start
之前发生的任何变量的任何读/写(我倾向于按照程序顺序在上面思考),也会在该Thread中的任何操作(它的run
方法)之前发生。
实际上,如果以前没有发生过(允许重新排序)并且程序执行允许那些潜在的重新排序,那么可能会发生破坏并抛出该错误。 我甚至倾向于说和弱内存模型使用适当的CPU(假设你在英特尔,这是一个强大的内存模型)可以增加这个机会,但我不确定。
因此,据我所知,这些操作将按以下顺序进行:首先,使用变量n
重新排序发布引用(之前没有发生,因此允许这样做)。 Thread1创建一个Holder
实例。 Thread2看到发布的引用并调用该方法。 它将变量n
读取zero
(记住重新排序发生且n
尚未写入,因此默认值zero
),因此它执行!=
检查, 但是创建Holder
Thread1将n
写入例如, 在 Thread2再次读取它之前 (在!=n
部分中)。 所以这可能会失败。
使价值final
可以解决这个问题,因为它会引入正确的记忆障碍,或者在规则之前发生 。