今天工作时,我在Java中遇到了volatile关键字。不是很熟悉,我找到了这个解释。

鉴于那篇文章详细解释了所讨论的关键字,您曾经使用过它吗?或者您曾经看到过以正确方式使用该关键字的情况吗?


当前回答

Volatile对于停止线程非常有用。

并不是说您应该编写自己的线程,Java 1.6有很多不错的线程池。但是如果你确定你需要一个线程,你需要知道如何停止它。

我使用的线程模式是:

public class Foo extends Thread {

  private volatile boolean close = false;

  public void run() {
    while(!close) {
      // do work
    }
  }
  public void close() {
    close = true;
    // interrupt here if needed
  }
}

在上面的代码段中,while循环中读取close的线程与调用close()的线程不同。如果没有volatile,运行循环的线程可能永远看不到关闭的更改。

注意,这里不需要同步

其他回答

每个访问volatile字段的线程在继续之前都会读取它的当前值,而不是(可能)使用缓存的值。

只有成员变量可以是易变的或瞬变的。

是的,当你想要一个可变变量被多个线程访问时,必须使用volatile。这不是很常见的用例,因为通常你需要执行不止一个原子操作(例如,在修改变量之前检查变量状态),在这种情况下,你会使用同步块代替。

如果您有一个多线程系统,这些多线程处理一些共享数据,这些线程将在它们自己的缓存中加载数据。如果我们不锁定资源,在一个线程中所做的任何更改都不会在另一个线程中可用。

使用锁定机制,我们可以添加对数据源的读/写访问。如果一个线程修改了数据源,该数据将存储在主存中而不是它的缓存中。当其他线程需要这个数据时,它们将从主存中读取它。这将显著增加延迟。

为了减少延迟,我们将变量声明为volatile。这意味着无论何时变量的值在任何处理器中被修改,其他线程都将被迫读取它。它仍然有一些延迟,但比从主存中读取要好。

Volatile变量是轻量级同步。当所有线程之间的最新数据可见性是必需的,并且原子性可能会受到损害时,在这种情况下,Volatile变量必须是首选。对volatile变量的读取总是返回任何线程最近完成的写操作,因为它们既不缓存在寄存器中,也不缓存在其他处理器看不到的缓存中。Volatile是无锁的。当场景满足上面提到的条件时,我使用volatile。

volatile关键字有两种不同的用法。

阻止JVM从寄存器(假设为缓存)读取值,并强制从内存读取其值。 降低内存不一致错误的风险。

阻止JVM读取寄存器中的值,并强制其读取 从内存中读取的值。

busy标志用于防止线程在设备繁忙且该标志不受锁保护的情况下继续运行:

while (busy) {
    /* do something else */
}

当另一个线程关闭busy标志时,测试线程将继续执行:

busy = 0;

但是,由于busy在测试线程中经常被访问,JVM可以通过将busy的值放在寄存器中来优化测试,然后在每次测试之前测试寄存器的内容,而不读取内存中的busy值。测试线程永远不会看到busy的变化,而另一个线程只会改变内存中的busy值,从而导致死锁。将busy标志声明为volatile将强制在每次测试之前读取它的值。

降低内存一致性错误的风险。

使用易失性变量可以降低内存一致性错误的风险,因为对易失性变量的任何写入都会建立一个 “happens-before”关系与该变量的后续读取之间的关系。这意味着对volatile变量的更改总是对其他线程可见。

这种读写而不产生记忆一致性错误的技术被称为原子动作。

原子作用是指有效地同时发生的作用。原子的动作不可能中途停止:它要么完全发生,要么根本不发生。在操作完成之前,原子操作的副作用是不可见的。

下面是你可以指定的原子操作:

对于引用变量和大多数变量来说,读和写都是原子的 基本变量(所有类型,除了long和double)。 对于所有声明为volatile的变量,读和写都是原子的 (包括长变量和双变量)。

干杯!