在这种情况下永远不会抛出这个 AssertionError 吗?
Posted
技术标签:
【中文标题】在这种情况下永远不会抛出这个 AssertionError 吗?【英文标题】:Will this AssertionError never be thrown in this case? 【发布时间】:2018-06-16 17:05:54 【问题描述】:首先是代码,来自 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 的线程,因此所有后续线程都可以安全地成为一个完全可见的 Holder,因为它具有happens-before 保证。我说的对吗?
我什至尝试运行这段代码很长时间,没有出现断言错误。
但是,如果 main 如下所示,那么我相信 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();
【问题讨论】:
你从哪里得到SafePublication
类代码?
我写了那个代码
那我不明白你的问题
实际上,SafePublication
并不安全,因为holder = new Holder(42);
是不安全的发布。
SafePublication的main方法中可以抛出AssertionError吗?
【参考方案1】:
是的,这是安全的,因为 Thread#start
保证 happens-before。说得更啰嗦:在Thread#start
之前发生的任何变量的任何读/写操作(如果你愿意,我倾向于按程序顺序高于 考虑),也将发生在之前该线程中的任何操作(它是run
方法)。
确实,如果之前没有发生过(允许重新排序)并且如果程序执行允许那些潜在的重新排序,那么这可能会发生中断并抛出该错误。我什至倾向于说和一个适当的 CPU 与弱内存模型(假设您使用的是英特尔,这是一个强内存模型)可以增加这种机会,但我不确定。
因此,据我所知,这些操作将按以下顺序进行:首先使用变量 n
重新排序引用的发布(没有发生之前,所以这是允许的) . Thread1 创建一个Holder
的实例。 Thread2 看到已发布的引用并调用该方法。它将变量n
读取为zero
(请记住,发生了重新排序并且n
尚未写入,因此默认值为zero
),因此它执行!=
检查,但是创建Holder
的线程1,将n
写入12
,例如之前线程2再次读取它(在!=n
部分)。所以这可能会失败。
设置final
的值可以解决这个问题,因为它引入了正确的内存屏障,或发生之前 规则。
【讨论】:
以上是关于在这种情况下永远不会抛出这个 AssertionError 吗?的主要内容,如果未能解决你的问题,请参考以下文章
为啥即使永远不会抛出 IOException 也可以在 java 7 中捕获 IOException