You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Thread.start()先行发生原则,第一种场景下AssertionError是否永不会抛出?

你的判断完全正确!咱们来仔细拆解两种场景下的线程安全逻辑,结合先行发生(happens-before)规则解释差异:

第一种main方法场景:永远不会抛出AssertionError

在第一个版本的main方法里,主线程先完整执行了t.initialize()——也就是把holder引用指向了一个已经完成初始化的Holder实例(new Holder(42)),之后才进入循环不断启动新线程调用t.holder.assertSanity()。

这里的核心是Thread.start()的先行发生保证:主线程在调用new Thread(...).start()之前的所有操作,都happens-before于该新线程中的任何操作。这意味着主线程对holder的赋值(以及Holder构造函数里对n的赋值),对后续启动的所有线程都是完全可见的。

所以每个新线程调用assertSanity()时,看到的n都是已经正确初始化的42,两次读取n的结果必然一致,永远不会触发n != n的断言错误。

第二种main方法场景:可能抛出AssertionError

修改后的main方法把t.initialize()放到了单独的线程里执行,同时主线程不断启动新线程去读取t.holder并调用assertSanity(),这直接打破了安全发布的前提:

  • 初始化holder的线程和读取holder的线程之间没有任何同步机制,也就没有对应的happens-before关系。
  • 由于JVM的指令重排序和缓存可见性特性,Holder的构造函数可能被重排序:先把holder的引用指向一块未完全初始化的内存(此时n还是默认值0),之后再执行this.n = n的赋值操作。
  • 如果某个读取线程刚好在这个重排序的间隙执行assertSanity(),第一次读取n拿到默认值0,第二次读取时n已经被赋值为42,就会出现0 != 42的情况,触发AssertionError。

这正是经典的不安全发布场景:没有通过正确的同步机制发布对象,导致其他线程看到对象的不完整状态。

内容的提问来源于stack exchange,提问作者katiex7

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 07:31:50