在Java或Kotlin等JVM语言中,每个线程都拥有自己的“本地内存”,也称为“缓存”。当一个线程想要修改一个变量时,它首先会在自己的缓存中更新这个值,然后将修改同步到主内存中,主内存是线程间共享的。
下面的示例展示了一个线程未察觉到变化发生的情况:
class VolatileExample3 {
var flag = false
var a = 0
fun write() {
a = 1
Thread.sleep(10) // 模拟IO操作的延迟
flag = true
}
companion object {
@JvmStatic
fun test() {
val example = VolatileExample3()
val th = Thread { example.write() }
th.start()
while (!example.flag) {}
println("a = ${example.a}")
}
}
}
在这个例子中,th
线程执行write()
方法,先更新a
的值,然后暂停10毫秒,最后设置flag
为true
,表示写操作已完成。
主线程不断检查flag
的值,直到它变为true
,理论上这时write()
应该已经完成。
我们期望输出是"a = 1",这是写操作完成后正确的结果。
但是,由于存在“本地内存/缓存”,主线程可能会在while
循环中看似卡住,因为它读取的是自己缓存中的flag
值,并没有看到主内存中flag
已被更新为true
。
解决这个问题的一个明显答案是在flag
上添加@Volatile
注解,这样在对它进行读操作时会先使本地内存失效,确保从主内存加载最新值。
但我发现另一个解决方法是修改while
循环如下:
while (!example.flag) {
print("")
}
这个方法不使用@Volatile
也能工作!我很好奇是否print()
函数内部的“刷新缓存”操作会导致主线程的本地内存失效。如果是这样,其背后的机制是什么?
另外,我还发现如果在while
循环前加上th.join()
也同样有效。这些方法本质上是否相同?即一个线程的终止会通知所有其他线程,导致它们的本地内存失效吗?