今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。
鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?
今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。
鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?
当前回答
是的,我经常使用它——它对多线程代码非常有用。你指的那篇文章很好。不过有两件重要的事情要记住:
You should only use volatile if you completely understand what it does and how it differs to synchronized. In many situations volatile appears, on the surface, to be a simpler more performant alternative to synchronized, when often a better understanding of volatile would make clear that synchronized is the only option that would work. volatile doesn't actually work in a lot of older JVMs, although synchronized does. I remember seeing a document that referenced the various levels of support in different JVMs but unfortunately I can't find it now. Definitely look into it if you're using Java pre 1.5 or if you don't have control over the JVMs that your program will be running on.
其他回答
从oracle文档页,需要volatile变量来修复内存一致性问题:
使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会与该变量的后续读取建立happens-before关系。
这意味着对volatile变量的更改总是对其他线程可见。这也意味着当线程读取一个volatile变量时,它不仅看到了volatile的最新更改,还看到了导致更改的代码的副作用。
正如在Peter Parker的回答中解释的那样,在没有volatile修饰符的情况下,每个线程的堆栈都可能有自己的变量副本。通过将变量设置为volatile,可以修复内存一致性问题。
为了更好地理解,请查看jenkov教程页面。
看一下相关的SE问题,了解更多关于volatile的细节和使用volatile的用例:
Java中volatile和synchronized的区别
一个实际的用例:
你有很多线程,它们需要以特定的格式打印当前时间,例如:java.text.SimpleDateFormat("HH-mm-ss")。可以有一个类,它将当前时间转换为SimpleDateFormat,并每一秒更新一次变量。所有其他线程都可以使用这个易失性变量在日志文件中打印当前时间。
在我看来,除了停止线程之外,使用volatile关键字的两个重要场景是:
Double-checked locking mechanism. Used often in Singleton design pattern. In this the singleton object needs to be declared volatile. Spurious Wakeups. Thread may sometimes wake up from wait call even if no notify call has been issued. This behavior is called spurious wakeup. This can be countered by using a conditional variable (boolean flag). Put the wait() call in a while loop as long as the flag is true. So if thread wakes up from wait call due to any reasons other than Notify/NotifyAll then it encounters flag is still true and hence calls wait again. Prior to calling notify set this flag to true. In this case the boolean flag is declared as volatile.
对于长变量和双变量类型的读写操作的处理,目前还没有人提及。读和写对于引用变量和大多数基本变量都是原子操作,长变量和双变量类型除外,它们必须使用volatile关键字作为原子操作。@link
挥发性(vɒlətʌɪl):在常温下容易挥发
关于volatile的重要一点:
Synchronization in Java is possible by using Java keywords synchronized and volatile and locks. In Java, we can not have synchronized variable. Using synchronized keyword with a variable is illegal and will result in compilation error. Instead of using the synchronized variable in Java, you can use the java volatile variable, which will instruct JVM threads to read the value of volatile variable from main memory and don’t cache it locally. If a variable is not shared between multiple threads then there is no need to use the volatile keyword.
源
volatile用法示例:
public class Singleton {
private static volatile Singleton _instance; // volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized (Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
我们在第一个请求到来时惰性地创建实例。
如果我们不使_instance变量为volatile,那么创建Singleton实例的线程就不能与其他线程通信。因此,如果线程A正在创建单例实例,在创建后,CPU损坏等,所有其他线程将无法看到_instance的值不为空,他们将认为它仍然被分配为空。
为什么会发生这种情况?因为读线程不做任何锁,直到写线程从同步块中出来,内存不会被同步,_instance的值也不会在主存中更新。使用Java中的Volatile关键字,这是由Java本身处理的,这样的更新将对所有读取线程可见。
结论:volatile关键字也用于线程之间的内存内容通信。
without volatile的用法示例:
public class Singleton {
private static Singleton _instance; //without volatile variable
public static Singleton getInstance() {
if (_instance == null) {
synchronized(Singleton.class) {
if (_instance == null)
_instance = new Singleton();
}
}
return _instance;
}
}
The code above is not thread-safe. Although it checks the value of instance once again within the synchronized block (for performance reasons), the JIT compiler can rearrange the bytecode in a way that the reference to the instance is set before the constructor has finished its execution. This means the method getInstance() returns an object that may not have been initialized completely. To make the code thread-safe, the keyword volatile can be used since Java 5 for the instance variable. Variables that are marked as volatile get only visible to other threads once the constructor of the object has finished its execution completely. Source
Java中不稳定的用法:
快速失败迭代器通常使用list对象上的volatile计数器实现。
当列表更新时,计数器会递增。 创建Iterator时,计数器的当前值嵌入到Iterator对象中。 当执行Iterator操作时,该方法比较两个计数器值,如果不相同则抛出ConcurrentModificationException异常。
故障安全迭代器的实现通常是轻量级的。它们通常依赖于特定列表实现的数据结构的属性。没有一般的模式。
Volatile具有内存可见性的语义。基本上,volatile字段的值在写操作完成后对所有读取器(特别是其他线程)可见。如果没有volatile,读者可以看到一些未更新的值。
回答您的问题:是的,我使用一个volatile变量来控制某些代码是否继续循环。循环测试易变值,如果为真则继续。可以通过调用“stop”方法将条件设置为false。循环看到false,并在stop方法完成执行后测试该值时终止。
我强烈推荐的《Java并发实践》一书对volatile做了很好的解释。这本书的作者与问题中提到的IBM文章的作者是同一人(事实上,他在那篇文章的末尾引用了他的书)。我对volatile的使用被他的文章称为“模式1状态标志”。
如果您想了解更多关于volatile在底层是如何工作的,请阅读Java内存模型。如果你想超越这个层次,看看像Hennessy & Patterson这样的优秀计算机体系结构书籍,阅读关于缓存一致性和缓存一致性的内容。