Java多线程:共享char数组是否会因竞态条件出现异常输出?
共享数组修改后的可见性问题解答
先直接给你吃个定心丸:你的代码绝对不会输出'aB'或'bA',最终一定会打印'AB'。下面来拆解背后的原因,包括你关心的JMM规则和缓存行疑问:
1. Future.get()触发的Happens-Before关系是核心
Java内存模型(JMM)里的happens-before规则直接解决了你的可见性担忧:
- 当你调用
f1.get()并成功返回时,JMM会强制保证:f1对应的任务里的所有操作(也就是shared[0] = 'A'),都绝对先于当前线程后续的任何操作执行完成,并且所有修改都同步到了主内存。 - 同理,
f2.get()返回后,f2任务的修改(shared[1] = 'B')也会完全同步,并且先于后续的打印操作。
这意味着,当你执行System.out.println(shared)时,两个任务对数组的修改已经100%对当前线程可见,所以输出必然是AB。
2. 缓存行刷新不会导致“陈旧字节覆盖”
你担心的“同一缓存行里的相邻字节被陈旧数据覆盖”是不会发生的:
- 现代CPU的缓存一致性协议(比如MESI)会妥善处理同一缓存行内不同字节的独立修改:线程修改某一字节后,缓存系统只会标记该字节所在的缓存行为“脏”,同步时只会把修改过的字节更新到主内存,不会覆盖其他线程已经修改的相邻字节。
- 退一步说,JMM本身已经通过happens-before规则屏蔽了底层缓存的细节——不管缓存怎么刷新,只要满足规则,你就能保证操作的可见性和正确性,不用纠结底层的缓存实现。
额外提一句:如果去掉get()会怎样?
要是你的代码没调用f1.get()和f2.get(),直接打印数组,那确实可能出现aB、bA甚至ab的情况,因为此时没有同步机制保证任务的修改对当前线程可见。但你的代码里明确等待了两个任务完成,所以完全没这个风险。
内容的提问来源于stack exchange,提问作者bhh1988
相关产品推荐
相关产品推荐

