协慌网

登录 贡献 社区

volatile 关键字有什么用?

今天的工作中,我遇到了 Java 中volatile我不太熟悉它,所以找到了这种解释

鉴于该文章详细解释了所讨论的关键字,您是否曾经使用过它,或者是否曾见过可以正确使用该关键字的情况?

答案

volatile 具有内存可见volatile基本上,对所有读取器(特别是其他线程)而言, volatile没有volatile ,读者可能会看到一些未更新的值。

要回答您的问题:是的,我使用一个volatile变量来控制某些代码是否继续循环。循环测试volatile值,如果为true继续。可以通过调用 “停止” 方法将条件设置为false在 stop 方法完成执行之后,该循环将看到false并在测试该值时终止。

我强烈推荐的一本书 “实践中的 Java 并发性” 很好地解释了volatile 。本书是由撰写该问题中引用的 IBM 文章的同一人撰写的(实际上,他在该书的底部引用了他的书)。我对volatile使用是他的文章所说的 “模式 1 状态标志”。

如果您想了解有关volatile在幕后如何工作的更多信息,请阅读Java 内存模型。如果您想超越该级别,请阅读 Hennessy&Patterson这样的优秀计算机体系结构书籍,并阅读有关高速缓存一致性和高速缓存一致性的信息。

“…volatile 修饰符保证读取字段的任何线程都将看到最新写入的值。” - 乔什 · 布洛赫(Josh Bloch)

如果您正在考虑使用volatile ,请阅读有关原子行为的java.util.concurrent

Wikipedia 上有关Singleton Pattern 的帖子显示使用情况不稳定。

关于volatile要点:

  1. synchronizedvolatile和 locks,可以在 Java 中进行同步。
  2. 在 Java 中,我们不能有synchronized变量。将synchronized关键字与变量一起使用是非法的,并且会导致编译错误。可以在 Java 中使用volatile变量,而不是在 Java 中使用synchronized变量,该变量将指示 JVM 线程volatile变量的值,而不是在本地对其进行缓存。
  3. 如果没有在多个线程之间共享变量,则无需使用volatile关键字。

来源

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 正在创建 Singleton 实例,并且在创建后,CPU 损坏等情况下,所有其他线程将无法看到_instance的值不为 null,并且他们会认为_instance 的值仍分配为 null。

为什么会这样?因为读取器线程未进行任何锁定,并且直到写入器线程从同步块中退出,否则内存将不同步,并且_instance值也不会在主内存中更新。使用 Java 中的 Volatile 关键字,此关键字由 Java 本身处理,并且此类更新将在所有读取器线程中可见。

结论volatile关键字还用于在线程之间传递内存的内容。

不使用 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;  
    }

上面的代码不是线程安全的。尽管出于性能原因,它会在同步块中再次检查实例的值(出于性能原因),但 JIT 编译器可以通过在构造函数完成执行之前设置对实例的引用的方式来重新排列字节码。这意味着方法 getInstance()返回的对象可能尚未完全初始化。为了使代码具有线程安全性,从 Java 5 开始,可以将关键字 volatile 用于实例变量。一旦对象的构造函数完全完成其执行,标记为 volatile 的变量仅对其他线程可见。
来源

在此处输入图片说明

Java 中的volatile用法:

故障快速迭代器通常使用列表对象上的volatile

  • 当列表更新时,计数器增加。
  • Iterator ,计数器的当前值将嵌入到Iterator对象中。
  • Iterator操作时,该方法将比较两个计数器值,如果ConcurrentModificationException

故障安全迭代器的实现通常是轻量级的。它们通常依赖于特定列表实现的数据结构的属性。没有一般模式。