易失性关键字线程安全

时间:2018-10-31 12:50:46

标签: java multithreading volatile

经过大量的搜索,我发现了volatile关键字的多个定义。

概念1:

某些网站说,它是线程安全的,因为线程在存储volatile关键字的主内存上起作用并对其进行修改,而无需将其拉到线程堆栈空间。

概念2:

有人说,它不是线程安全的,因为它会导致线程竞争状态。作为,线程将volatile变量拉到堆栈空间,对其进行修改,然后立即将其放回主内存。但是,在另一个线程之间可以对volatile变量起作用并采取行动。因此,这种方式会失去一些价值。

哪个概念正确?

5 个答案:

答案 0 :(得分:4)

volatile既不是线程安全的,也不是非线程安全的。

volatile对单个字段保证atomicity,因此它可用于单个线程安全的读取或单个线程安全的写入。

但是,如果您要执行由读取和写入(整体上可以理解)组成的线程安全操作,则volatile并不能提供线程安全,因为volatile保证了原子性仅用于单个操作(读或写)。

总结:

  • 如果您有一个字段,并且想要确保如果一个线程向其写入,则其他线程可以立即读取写入的值-volatile就足够了(如果没有volatile,其他线程可能永远不会甚至看不到书面价值)
  • 如果您有一个需要先读取然后才写入的字段(基于您刚刚读取的值,因此在此值之间不会发生任何操作),volatile是不够的;相反,您可以使用:

答案 1 :(得分:3)

这篇IBM文章很好地总结了这一点:

  

易失性变量共享已同步的可见性功能,但是   没有一个原子性特征。这意味着线程将   自动查看易失性变量的最新值。   它们可用于提供线程安全性,但仅在   受限制的情况:在情况之间不施加约束的情况   多个变量或变量的当前值与其变量之间   未来价值。因此,仅凭挥发性就不足以实施   计数器,互斥量或任何具有不变量相关的类   多个变量(例如“ start <= end”)。

来源:https://www.ibm.com/developerworks/java/library/j-jtp06197/

与StackOverflow相关的问题:Do you ever use the volatile keyword in Java?

答案 2 :(得分:2)

concept 1很容易理解,但是不正确。 volatile是JMM提供的某种同步规范,与实现无关。此外,它不能在所有情况下保证线程安全。

  

使用易失性变量可降低内存一致性的风险   错误,因为任何对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.

答案 3 :(得分:2)

在这里,我经过全面的Google搜索后了解了什么:

•Volatile关键字用于避免线程内部变量的本地副本。所有线程将从主内存访问volatile变量,对其进行更新,然后立即将其放回主m / m。

来自ibm.com::所有读写操作将直接对主m / m进行,而不是对寄存器或本地处理器高速缓存进行。

•易失变量不是线程安全的。如果多个线程尝试写入一个volatile变量,则会出现Race条件。

•每个线程都有一个单独的m / m空间,称为工作m / m,其中包含diff的值。用于执行操作的变量。执行完操作后,线程将变量的更新值复制到main m / m中,其他线程可以从那里读取最新值。但是,如果变量是非易失性的,则新值可能不会立即刷新到主m / m。其他线程可能无法读取更新的值。

•同步提供互斥和可见性。但是volatile仅提供可见性。

答案 4 :(得分:0)

仅使用volatile不会使关键部分成为线程安全。 只是它以两种方式帮助我们。

  1. 线程可以拥有内存位置值的副本(可能已加载到注册表或缓存)。当访问volatile变量时,它将始终从主内存中读取。

  2. java编译器不会重新排列这些变量的使用顺序(特别是在同步范围内)。

这意味着,要编写线程安全代码,我们可能必须使用volatile,但仅使用volatile并不会带来任何好处。