jvm print()使线程本地缓存无效?

bksxznpy  于 5个月前  发布在  其他
关注(0)|答案(2)|浏览(50)

在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循环之前也可以。这些方法本质上是一样的吗?这意味着一个线程的终止会被所有其他线程注意到,从而导致本地内存失效?

8aqjt8rx

8aqjt8rx1#

如果我在while循环中加入th.join(),也会导致主线程缓存失效。为什么会这样?

ygya80vv

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()调用返回之前。

相关问题