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

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


当前回答

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

其他回答

Volatile具有内存可见性的语义。基本上,volatile字段的值在写操作完成后对所有读取器(特别是其他线程)可见。如果没有volatile,读者可以看到一些未更新的值。

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

我强烈推荐的《Java并发实践》一书对volatile做了很好的解释。这本书的作者与问题中提到的IBM文章的作者是同一人(事实上,他在那篇文章的末尾引用了他的书)。我对volatile的使用被他的文章称为“模式1状态标志”。

如果您想了解更多关于volatile在底层是如何工作的,请阅读Java内存模型。如果你想超越这个层次,看看像Hennessy & Patterson这样的优秀计算机体系结构书籍,阅读关于缓存一致性和缓存一致性的内容。

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

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

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

The volatile key when used with a variable, will make sure that threads reading this variable will see the same value . Now if you have multiple threads reading and writing to a variable, making the variable volatile will not be enough and data will be corrupted . Image threads have read the same value but each one has done some chages (say incremented a counter) , when writing back to the memory, data integrity is violated . That is why it is necessary to make the varible synchronized (diffrent ways are possible)

如果修改是由一个线程完成的,而其他线程只需要读取这个值,则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,运行循环的线程可能永远看不到关闭的更改。

注意,这里不需要同步

挥发性(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异常。

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