在Java/Kotlin或任何JVM语言中,每个线程都有一个“本地内存”,也就是“缓存”。当一个线程想要将一个变量写入内存时,它首先更新自己缓存中的值,然后将修改同步到主内存中,主内存在线程之间共享。
下面的demo演示了一个发生线程未实现更改的用例:
class VolatileExample3 {
var flag = false
var a = 0
fun write() {
a = 1
Thread.sleep(10)
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 ms作为模拟IO操作,最后将flag设置为true,表示“写”已经完成。
在主线程中,它总是在flag
为true时查询flag的值并跳过while循环,这应该是write()
结束的时间。
我们希望输出为“a = 1”,这是写入后的正确答案。
但是,由于存在“本地内存/缓存”,主线程将在while
中循环,就好像它被卡住了一样。
Main thread won't read Main Memory
在write()
的内容运行之前,主线程首先读取flag
并获得false,直到我们显式地告诉它,它才会获得Main Memory中的值。
这个问题的答案很明显:将@Volatile
添加到flag
,其中READ
操作将使其本地内存在真实的LOAD VALUE
之前无效。
但我发现了这个问题的另一个解决方案,那就是:
while (!example.flag) {
print("")
}
型
它不需要@Volatile
就可以工作!我想知道print()
内部的flush cache
操作是否会使主线程的本地内存/缓存无效。如果是这样,机制是什么?
我还发现把th.join()
放在while循环之前也可以。这些方法本质上是一样的吗?这意味着一个线程的终止会被所有其他线程注意到,从而导致本地内存失效?
2条答案
按热度按时间8aqjt8rx1#
如果我在while循环中加入th.join(),也会导致主线程缓存失效。为什么会这样?
ygya80vv2#
Java编程语言中没有“缓存”,缓存是底层计算机硬件架构的一部分,它们在不同的计算机上工作方式不同。
作为Java程序员,您的工作是确保您的代码遵守the Java Language Specification(JLS)中发布的规则。(
javac
)、Java虚拟机(JVM)和Java运行时环境(JRE)的目的是确保如果您的代码遵守JLS中的规则,那么 * 他们的 * 代码将保持JLS做出的承诺。上次我看过,关于从多线程访问变量的规则和promise主要在第17章中,主要是用“同步”和“发生在”关系来表达的。
我找到了这个问题的另一个解决方案:...
print()
这可能适用于 * 您的 * 计算机,使用您今天碰巧使用的操作系统版本和JDK版本。事实上,它适用于很多人和许多不同的计算机,但它不能 * 保证 * 工作。
实验并不是找出什么“有效”什么不有效的方法。我曾经为一家大公司工作,该公司发布了一个违反规则的主要软件包。它在我们的办公室通过了几个星期的积极测试。当我们第一次发布它时,它甚至在我们所有客户的计算机上都“有效”。但是后来,我们的一个客户升级了他们的操作系统,软件对他们停止工作了。
另外,我发现把th.join()放在while循环之前也可以。
JLS显式地保证线程A对共享变量所做的任何事情都必须在其他线程调用
A.join()
之后对其他线程可见。在JLS的语言中,线程A所做的一切都“发生在”其他线程中的A.join()
调用返回之前。