可变关键字线程安全[重复]
Posted
技术标签:
【中文标题】可变关键字线程安全[重复]【英文标题】:Volatile keyword Thread Safety [duplicate] 【发布时间】:2019-04-04 15:27:35 【问题描述】:在谷歌上搜索了很多之后,我发现了 volatile 关键字的多个定义。
概念 1:
一些网站说,它是线程安全的,因为线程作用于存储 volatile 关键字的主内存并对其进行修改,而不会将其拉入线程堆栈空间。
概念2:
有人说,它不是线程安全的,因为它会导致线程竞争条件。 As ,线程将 volatile 变量拉入堆栈空间,对其进行修改并立即将其放回主存。但是,在另一个线程之间可以来对 volatile 变量采取行动并采取行动。所以,这样一来,某些值就会丢失。
哪个概念是正确的?
【问题讨论】:
两者都可能是正确的,这取决于 volatile 变量的使用方式。 既然您询问的是 Java,请确保您阅读的是 Java 特定的解释。volatile
关键字在不同的编程语言中有不同的含义。
【参考方案1】:
volatile
本身既不是线程安全的,也不是非线程安全的。
volatile
保证 atomicity 用于单个字段,因此可用于单线程安全读取或单线程安全写入。
然而,如果你想执行一个线程安全的操作,包括读后写(作为一个整体来理解),volatile
单独在这里并不能提供线程安全,因为volatile
只保证单个操作的原子性(读或写)。
总结一下:
如果你有一个字段并且你想确保如果一个线程写入它,其他线程可以立即读取写入的值 -volatile
就足够了(没有volatile
,其他线程可能永远看不到写入的价值)
如果您有一个需要先读取然后写入的字段(基于您刚刚读取的值,因此在这之间可能不会对该值进行任何操作),volatile
是不够的;相反,您可以使用:
AtomicReference
、AtomicInteger
等
或synchronization。
【讨论】:
【参考方案2】:这篇 IBM 文章很好地总结了这一点:
易失性变量共享同步的可见性特征,但 没有原子性特征。这意味着线程将 自动查看 volatile 变量的最新值。 它们可以用来提供线程安全,但只能在非常 受限的案例集:那些不施加约束的案例 多个变量或变量的当前值与其 未来的价值。所以 volatile 本身并不足以实现一个 计数器、互斥体或任何具有相关不变量的类 多个变量(例如“start
来源:https://www.ibm.com/developerworks/java/library/j-jtp06197/
与 *** 相关的问题:Do you ever use the volatile keyword in Java?
【讨论】:
【参考方案3】:concept 1
很容易理解,但它是不正确的。 volatile
是 JMM 提供的某种同步规范,与实现无关。此外,它不能保证在所有情况下都是线程安全的。
使用 volatile 变量可降低内存一致性的风险 错误,因为对 volatile 变量的任何写入都会建立 与后续读取相同的发生之前的关系 多变的。
这意味着对 volatile 变量的更改总是 对其他线程可见。更重要的是,这也意味着当一个 线程读取一个 volatile 变量,它看到的不仅仅是最新的变化 易变的,但也导致代码的副作用 改变。
线程安全使用:
1. int value = 1;
2. thread1 executes: value = 2;
3. when thread2 reads value, it must be 2
线程不安全使用:
1. int value = 1;
2. thread1 executes: value++;
3. when thread2 reads value, it could be 1 or 2, because value++ is not atomic.
【讨论】:
【参考方案4】:这是我在彻底谷歌搜索后理解的:
• Volatile 关键字用于避免变量在线程内的本地副本。所有线程都将从主内存访问 volatile 变量,对其进行更新并立即将其放回主 m/m。
来自 ibm.com:所有读取和写入都将直接发送到主 m/m,而不是寄存器或本地处理器缓存。
• 易失变量不是线程安全的。如果多个线程尝试写入 volatile 变量,则会发生竞争条件。
• 每个线程都有一个单独的 m/m 空间,称为工作 m/m,它保存 diff 的值。用于执行操作的变量。执行操作后,线程将变量的更新值复制到主 m/m,其他线程可以从那里读取最新值。但是,如果变量是非易失性的,那么新值可能不会立即刷新到主 m/m。并且其他线程可能不会读取更新后的值。
• 同步提供互斥和可见性。但 volatile 仅提供可见性。
【讨论】:
【参考方案5】:仅使用 volatile 不会使临界区成为线程安全的。 只是它在两个方面帮助我们。
线程可以拥有自己的内存位置值副本(可能加载到注册表或缓存)。当访问 volatile 变量时,它总是从主内存中读取。
java 编译器不会重新排序这些变量的使用(特别是从同步范围)。
这意味着,要编写线程安全的代码,我们可能必须使用 volatile,但仅使用 volatile 并没有任何好处。
【讨论】:
以上是关于可变关键字线程安全[重复]的主要内容,如果未能解决你的问题,请参考以下文章