volatile 有个疑惑

2022-12-03 14:28:15 +08:00
 fhj

看网上都说,线程里面保存着变量的副本,无法感知其他线程对共享变量的修改。 由于 p 没有用 volatile 修饰,while 会一直执行,但实际运行显示,即使没有 volatile 线程也会立即感知到变量的修改,是什么原因呀。 var p = false Thread({ while (!p) { println(1) } })

p = true;
Thread.sleep(1000)
11486 次点击
所在节点    Android
26 条回复
b1ghawk
2022-12-03 21:00:26 +08:00
@leonshaw 长话短说,也就是 volatile 防重排的能力并不来自于其本身,而是来自于另一套 happens-before 机制,这个机制除了处理 volatile 还处理了其它的情景。
zhangdszq
2022-12-03 21:50:09 +08:00
@fhj 不完全是。volatile 关键字除了可以防止指令重排优化之外,它还有一些其他用途。首先,它确保了线程能够立即感知到变量的修改。这意味着,如果一个线程修改了一个 volatile 变量的值,其他线程能够立即感知到这个修改,而不是等到它们下一次访问该变量时才感知到。

另外,volatile 关键字还可以用于确保多线程对于共享变量的可见性。由于线程在执行过程中可能会缓存变量的值,因此,如果没有特殊指定,其他线程可能无法立即感知到某个线程对变量的修改。如果变量被 volatile 修饰,线程在修改变量时会自动清空缓存,以确保其他线程能够立即感知到变量的修改。

--- ChatGPT
leonshaw
2022-12-03 21:54:54 +08:00
@b1ghawk 你说的可能是其它语言的 volatile 。我的意思是 Java 的 volatile 关键字包含了 happens-before 的语义,不是概念本身的包含。两方面,Java 里对 volatile 变量的写是一个 store-release ,读是 load-acquire 。也就是 Java 的 volatile 相当于 C 的 volatile 加上 happens-before 语义。
littlewing
2022-12-03 21:57:08 +08:00
java 的 volatile 自带了 fence 语义,可以保证内存可见性

你说的是 C/C++ 中的 volatile ,只保证 volatile 不缓存 register ,volatile 不被编译优化,编译阶段 volatile 之间的顺序;不保证内存可见性,volatile 和普通变量之间的顺序,即没有 fence 语义
littlewing
2022-12-03 21:58:35 +08:00
另外,没事别用 volatile ,直接用原子变量就行了
iseki
2022-12-04 10:21:03 +08:00
@fhj 所谓的线程本地副本,是为了和现实世界中变量可能被优化掉 /存到寄存器里 /跑到高速缓存里等情况对应。volatile 保证“可靠一致的访问”( JLS 原话),不是说不加就一定不一致了 (以上那些情况不一定会发生)
你这个例子就是因为种种原因,以上那些情况没发生,或者不良的副作用被别的因素抵消了

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/899799

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX