Java中的volatile关键字究竟是什么?

9
我与我的老师就Java中的"volatile"关键字有了一些争论。声明变量为"volatile"时,可以说:

这个变量的值永远不会被本地线程缓存:所有的读写操作将直接处理在 "主内存" 中。

但我的老师持有不同观点:

"volatile"关键字并没有保证变量的值会被保存在主内存中。

请问有谁能解决我们的分歧?谢谢!

这个问题可能会有所帮助:https://dev59.com/mnE95IYBdhLWcg3wKq2q - niceman
我相信volatile所做的就是让所有线程看到变量的一个副本(而不是每个线程都有多个副本),这个变量存储在哪里可能是一个实现细节(你的老师可能是正确的 :))。 - niceman
答案可能取决于你是在争论Java语言还是在争论Java语言的某个特定实现。在Java语言规范中,不存在缓存或主内存这样的东西。在Java中有对象、字段和变量,而volatile关键字确保了一个线程对变量所做的更新何时以及以什么顺序对其他线程可见。 - Solomon Slow
4个回答

10

如果要了解诸如 volatile 之类的关键字的确切含义,请查看Java语言规范,以查看官方含义。

JLS第8.3.1.4段解释了 volatile 的含义:

可以声明字段为volatile,在这种情况下,Java内存模型确保所有线程都看到变量的一致值(§17.4)。

第17.4节解释了Java内存模型。内存模型告诉您程序中每个语句执行后数据发生的一些保证。

仔细研究后,你会发现 volatile 的意思是,如果你写入一个易失性变量,你就有保证其他线程会看到写入的内容。实际上如何实现这一点并没有被明确指定。它可能通过强制写入主内存来实现,但JVM实现者可以自由选择不同的、可能更有效的机制。

因此,严格来说,你的老师是正确的。这并不一定意味着值被写入主内存;虽然在实践中,这可能是这种情况——但这取决于特定的JVM实现。


有没有其他更有效地实现volatile的方法? - MaxNevermind

2
我只引用文档中的两段话: first 一个字段可以被声明为volatile,这样Java内存模型就可以确保所有线程都看到该变量的一致值(§17.4)。 second 内存模型描述了程序可能的行为。实现可以自由地生成任何代码,只要程序的所有结果都可以通过内存模型预测。
这为实现者提供了很大的自由度,可以执行各种代码转换,包括重新排序操作和删除不必要的同步。
如果你查阅大多数关于Java中votatile关键字的教程,大部分都会建议与你提出的相同,即:该变量的值永远不会被本地线程缓存:所有读写操作都将直接到达“主内存”
但是文档并没有对此做出保证。虽然它也从未否认过这一点,只是说这是可能的行为
因此,我认为你的老师是正确的。根据文档(而不是除文档以外的大多数教程),Volatile关键字绝对不能保证变量的值将保存在主内存中。
然而,这并不意味着你是错误的,这完全取决于JVM的实现。

1
volatile关键字的作用是忽略所有本地CPU缓存,或者等效地说,刷新包含该volatile变量的缓存行以从主内存刷新。因此,正如文档所建议的那样,它确保所有读取直接从主内存读取,并且所有写入直接写入主内存。
除此之外,volatile还具有原子操作保证。它确保使用此关键字前缀的变量将被读取或写入为单个指令。因此,在某些特定情况下,例如在32位机器上使用64位长整型的易失性,JVM可能需要执行更多操作。有一些更多的文档可以在https://blogs.oracle.com/dave/entry/java_memory_model_concerns_on找到。
对于您的具体问题。
在Java中读取volatile时,它确保变量的值来自主内存。
在Java中写入volatile时,它确保变量的值被写入主内存。
上述内容是从“逻辑”角度讲解的。实际上,现代CPU采用复杂的缓存策略,有时可以在不实际写入主内存的情况下保证上述内容是正确的。但这只是一种性能优化步骤,当意识到可以绝对实现相同效果时才会这样做。但我认为这超出了讨论范围。

0

具体来说,语言规范没有提到“主内存”或线程本地缓存。尽管如此:“所有实例字段、静态字段和数组元素都存储在堆内存中”。 - Software Engineer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接