在这种情况下,是否会抛出此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可以解决这个问题,因为它会引入正确的记忆障碍,或者规则之前发生