Q1:这里的 i 没有 volatile ,线程 b 不能看到吧?
是的。
但是,作者那句话,开头有个关键词:“假设”。作者的意思是,已经假设 操作 A 发生在 操作 B 前面了。所以这里有没有 volatile 已经无关紧要。
Q2:这个是错误的吧?
是的。这里有两个问题。
第一个问题:
Happens Before 原则,只提到同一个线程内的连续上下文,并没有提跨方法、跨线程的问题。
我怀疑这篇文章的作者,是不是看了这篇英文资料:
https://www.geeksforgeeks.org/happens-before-relationship-in-java/这篇资料里 class ClassRoom 下面有两个方法,需要用不同线程,去同时执行。如果单独看任意一个方法的内部代码与执行顺序,这篇资料讨论的结论是对的。但,如果同时看两个线程、两个方法的并行关系,这篇资料的结论就完全错了,错误原因,涉及到太多东西,过于复杂,就不展开了,但我还是举个简单的反例,Thread 1 对应的方法 submitAssignment( ) ,内部 this.assgn = assgn; 这行代码,对 this.assgn 进行改变,直到方法结束后,这次改变的 new value ,是有可能,被一直保留在线程所在 CPU 的 Cache 或 当前 Thread Stack 内部,没有改动到 HEAP 的位置,Thread 2 自然就读不到该变量的 new value 。
问题 2:
这篇英文资料,说的是两个线程,同时执行。但楼主发的这篇中文资料,文章作者偷偷地作弊了,仔细看,他写的是:
线程 A 执行 writer( ) 方法之后,然后线程 B 才去执行 reader( ) 方法....
关键词:之后。
大多数正常情况下,writer( ) 执行完毕后,a 与 flag 的 new Value 都已经被 flush 到 HEAP 了,这个例子与 Happens Before 还有毛线关系?作者这例子明显是在坑小白。
不过也要注意到,就像在问题 1 中,我举得那个例子一样。特殊情况下,writer( ) 执行完毕后,a 与 flag 的 new Value ,是有可能,被一直保留在线程所在 CPU 的 Cache 或 Thread Stack 内部,没有改动到 HEAP 的位置,因此后续线程 B 执行的 reader( )方法里,就读不到这两个变量的 new value 。
建议:
这个问题的本质,涉及到太多知识。如果楼主想弄清楚,建议学习:
A.CPU 在执行代码与同步方面的知识。
B.汇编在同步方面的知识。
C.C++是如何处理这个问题的。