可变关键字线程安全[重复]

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 是不够的;相反,您可以使用: AtomicReferenceAtomicInteger等 或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 并没有任何好处。

【讨论】:

以上是关于可变关键字线程安全[重复]的主要内容,如果未能解决你的问题,请参考以下文章

如何保证线程安全?

如何做到类的线程安全

如何保证集合是线程安全的?

线程安全性

线程安全性

Java多线程——不变性与安全发布