你到底什么时候在 Java 中使用 volatile 关键字?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3488703/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 01:14:30  来源:igfitidea点击:

When exactly do you use the volatile keyword in Java?

javamultithreadingvolatile

提问by Ricket

I have read "When to use 'volatile' in Java?" but I'm still confused. How do I know when I should mark a variable volatile? What if I get it wrong, either omitting a volatile on something that needs it or putting volatile on something that doesn't? What are the rules of thumb when figuring out what variables should be volatile in multithreaded code?

我已经阅读了“何时在 Java 中使用 'volatile'?”但我仍然感到困惑。我怎么知道什么时候应该将变量标记为 volatile?如果我弄错了,要么在需要它的东西上省略 volatile,要么将 volatile 放在不需要它的东西上呢?在确定多线程代码中哪些变量应该是 volatile 时,经验法则是什么?

采纳答案by Enno Shioji

You basically use it when you want to let a member variable be accessed by multiple threads but do not need compound atomicity (not sure if this is the right terminology).

当您想让多个线程访问成员变量但不需要复合原子性(不确定这是否是正确的术语)时,您基本上会使用它。

class BadExample {
    private volatile int counter;

    public void hit(){
        /* This operation is in fact two operations:
         * 1) int tmp = this.counter;
         * 2) this.counter = tmp + 1;
         * and is thus broken (counter becomes fewer
         * than the accurate amount).
         */
        counter++;
    }
}

the above is a bad example, because you needcompound atomicity.

上面是一个不好的例子,因为你需要复合原子性。

 class BadExampleFixed {
    private int counter;

    public synchronized void hit(){
        /*
         * Only one thread performs action (1), (2) at a time
         * "atomically", in the sense that other threads can not 
         * observe the intermediate state between (1) and (2).
         * Therefore, the counter will be accurate.
         */
        counter++;
    }
}

Now to a valid example:

现在来看一个有效的例子:

 class GoodExample {
    private static volatile int temperature;

    //Called by some other thread than main
    public static void todaysTemperature(int temp){
        // This operation is a single operation, so you 
        // do not need compound atomicity
        temperature = temp;
    }

    public static void main(String[] args) throws Exception{
        while(true){
           Thread.sleep(2000);
           System.out.println("Today's temperature is "+temperature);
        }
    }
}

Now, why can't you just use private static int temperature? In fact you can (in the sense that that your program won't blow up or something), but the change to temperatureby the other thread may or may not be "visible" to the main thread.

现在,你为什么不能使用private static int temperature?事实上,您可以(从某种意义上说您的程序不会炸毁或其他东西),但是temperature其他线程对主线程的更改可能“可见”,也可能不“可见”。

Basically this means that it is even possible that your app. keeps writing Today's temperature is 0forever if you don'tuse volatile(in practice, the value tends to become eventually visible. However, you should not risk not using volatile when necessary, since it can lead to nasty bugs (caused by in-completely constructed objects etc.).

基本上这意味着您的 app. Today's temperature is 0如果您使用volatile,则永远保持写入(实际上,该值往往最终会变得可见。但是,您不应该冒险在必要时不使用 volatile,因为它会导致令人讨厌的错误(由不完全构造的对象等引起)。 )。

If you put volatilekeyword on something that doesn't need volatile, it won't affect your code's correctness (i.e. the behaviour will not change). In terms of performance, it will depend on the JVM implementation. In theory you might get a tiny performance degradation because the compiler can't do reordering optimisations, have to invalidate CPU cache etc., but then again the compiler could prove that your field cannot ever be accessed by multiple threads and remove the effect of volatilekeyword completely and compile it to identical instructions.

如果你把volatile关键字放在不需要的东西上volatile,它不会影响你的代码的正确性(即行为不会改变)。在性能方面,它将取决于 JVM 实现。从理论上讲,您可能会因为编译器无法进行重新排序优化、必须使 CPU 缓存无效等而导致性能略有下降,但是编译器可以再次证明您的字段永远无法被多个线程访问并消除volatile关键字的影响完全并将其编译为相同的指令。

EDIT:
Response to this comment:

编辑:
对此评论的回应:

Ok, but why can't we make todaysTemperature synchronized and create a synchronized getter for temperature?

好的,但是为什么我们不能使 todaysTemperature 同步并为温度创建一个同步的 getter 呢?

You can and it will behave correctly. Anything that you can with volatilecan be done with synchronized, but not vice versa. There are two reasons you might prefer volatileif you can:

你可以而且它会正确运行。您可以使用的任何东西都可以使用volatile来完成synchronized,但反之则不行。volatile如果可以,您可能更喜欢以下两个原因:

  1. Less bug prone: This depends on the context, but in many cases using volatileis less prone to concurrency bugs, like blocking while holding the lock, deadlocks etc.
  2. More performant: In most JVM implementations, volatilecan have significantly higher throughput and better latency. However in most applications the difference is too small to matter.
  1. 不易出错:这取决于上下文,但在许多情况下,使用volatile不太容易出现并发错误,例如在持有锁时阻塞、死锁等。
  2. 更高性能:在大多数 JVM 实现中,volatile可以具有显着更高的吞吐量和更好的延迟。然而,在大多数应用中,差异很小,无关紧要。

回答by KristofMols

http://mindprod.com/jgloss/volatile.html

http://mindprod.com/jgloss/volatile.html

"The volatile keyword is used on variables that may be modified simultaneously by other threads."

“ volatile 关键字用于可能被其他线程同时修改的变量。”

"Since other threads cannot see local variables, there is never any need to mark local variables volatile. You need synchronized to co-ordinate changes to variables from different threads, but often volatile will do just to look at them."

“由于其他线程无法看到局部变量,因此永远不需要将局部变量标记为 volatile。您需要同步来协调来自不同线程的变量的更改,但通常 volatile 只是为了查看它们。”

回答by mdma

Volatile is most useful in lock-free algorithms. You mark the variable holding shared data as volatile when you are not using locking to access that variable and you want changes made by one thread to be visible in another, or you want to create a "happens-after" relation to ensure that computation is not re-ordered, again, to ensure changes become visible at the appropriate time.

Volatile 在无锁算法中最有用。当您不使用锁定来访问该变量并且您希望一个线程所做的更改在另一个线程中可见时,您将保存共享数据的变量标记为 volatile,或者您希望创建一个“发生后”关系以确保计算是不再重新排序,以确保更改在适当的时间变得可见。

The JMM Cookbookdescribes which operations can be re-ordered and which cannot.

JMM食谱描述了可以操作的重新排序,哪些不能。

回答by Johannes Wachter

The volatilecan also be used to safely publish immutable objects in a multi-threaded Environment.

volatile还可以用来安全地发布在多线程环境中不可变对象。

Declaring a field like public volatile ImmutableObject foosecures that all threads always see the currently available instance reference.

声明一个像public volatile ImmutableObject foosecures这样的字段可以确保所有线程总是看到当前可用的实例引用。

See Java Concurrency in Practicefor more on that topic.

有关该主题的更多信息,请参阅Java 并发实践

回答by programmerravi

Actually disagree with the example given in the top voted answer, to my knowledge it does NOTproperly illustrate volatile semantics as per the Java memory model. Volatile has way more complex semantics.

在上面给出的例子其实不同意投票的答案,就我所知,它恰当地说明了可变语义按照Java存储模型。Volatile 有更复杂的语义。

In the example provided, the main thread could continue to print "Today's temperature is 0" forever even if there is another thread running that is supposed to update the temperature if that other thread never gets scheduled.

在提供的示例中,主线程可以永远继续打印“今天的温度为 0”,即使有另一个线程正在运行,如果另一个线程从未被调度,则应该更新温度。

A better way to illustrate volatile semantics is with 2 variables.

说明 volatile 语义的更好方法是使用 2 个变量。

For simplicity's sake, we will assume that the only way to update the two variables is through the method "setTemperatures".

为简单起见,我们假设更新这两个变量的唯一方法是通过方法“setTemperatures”

For simplicity's sake, we will assume that only 2 threads are running, main thread and thread 2.

为简单起见,我们假设只有 2 个线程在运行,主线程和线程 2。

//volatile variable
private static volatile int temperature; 
//any other variable, could be volatile or not volatile doesnt matter.
private static int yesterdaysTemperature
//Called by other thread(s)
public static void setTemperatures(int temp, int yestemp){
    //thread updates yesterday's temperature
    yesterdaysTemperature = yestemp;
    //thread updates today's temperature. 
    //This instruction can NOT be moved above the previous instruction for optimization.
    temperature = temp;
   }

the last two assignment instructions can NOTbe reordered for optimization purposes by either the compiler, runtime or the hardware.

编译器、运行时或硬件都不能出于优化目的对最后两条赋值指令进行重新排序。

public static void main(String[] args) throws Exception{
    while(true){
       Thread.sleep(2000);
       System.out.println("Today's temperature is "+temperature); 
       System.out.println("Yesterday's temperature was "+yesterdaysTemperature );
 }
}

Once the main thread reads the volatile variable temperature (in the process of printing it),

一旦主线程读取 volatile 变量温度(在打印过程中),

1) There is a guarantee that it will see the most recently writtenvalue of this volatile variable regardless of how many threads are writing to it, regardless of which method they are updating it in, synchronized or not.

1) 保证它会看到这个 volatile 变量最近写入的值,而不管有多少线程正在写入它,不管它们用哪种方法更新它,同步与否。

2) If the system.out statement in the main thread runs, afterthe time instant at which thread 2 has run the statement temperature = temp, both yesterday's temperature and todays temperature will be guaranteed to print the values set in them by thread 2 when it ran the statement temperature=temp.

2)如果主线程中的system.out语句运行,线程2运行语句温度=temp的时刻之后,昨天的温度和今天的温度都将保证打印线程2在其中设置的值,当它运行语句温度=温度。

This situation gets a LOTmore complex if a) Multiple threads are running and b) There are other methods than just the setTemperatures method that can update the variable yesterday's temperature and todays temperature that are actively being called by these other threads. I think it would take a decent size article to analyze the implications based on how the Java Memory Model describes the volatile semantics.

这种情况得到了LOT如果更复杂)多线程运行和b)有其他方法不仅仅是setTemperatures方法可以更新正在积极通过这些其他线程称为变量昨天的温度和今天的温度。我认为需要一篇体面的文章来分析基于 Java 内存模型如何描述 volatile 语义的含义。

In short, attempting to just use volatile for synchronization is extremely risky, and you would be better off sticking to synchronizing your methods.

简而言之,尝试仅使用 volatile 进行同步是非常危险的,您最好坚持同步您的方法。

回答by Ravindra babu

volatilekeyword guarantees that value of the volatile variable will always be read from main memory and not from Thread's local cache.

volatile关键字保证 volatile 变量的值总是从主内存中读取,而不是从线程的本地缓存中读取。

From java concurrency tutorial :

来自java并发教程

Using volatile variables reduces the risk of memory consistency errors, because any write to a volatile variable establishes a happens-before relationship with subsequent reads of that same variable

使用 volatile 变量可以降低内存一致性错误的风险,因为对 volatile 变量的任何写入都会与对该相同变量的后续读取建立先发生关系

This means that changes to a volatile variable are always visible to other threads. It also means that when a thread reads a volatile variable, it sees not just the latest change to the volatile, but also the side effects of the code that led up the change.

这意味着对 volatile 变量的更改始终对其他线程可见。这也意味着,当线程读取 volatile 变量时,它不仅会看到对 volatile 的最新更改,还会看到导致更改的代码的副作用。

Regarding your query:

关于您的查询:

How do I know when I should mark a variable volatile? What are the rules of thumb when figuring out what variables should be volatile in multithreaded code?

我怎么知道什么时候应该将变量标记为 volatile?在确定多线程代码中哪些变量应该是 volatile 时,经验法则是什么?

If you feel that all reader threads always get latest value of a variable, you have to mark variable as volatile

如果你觉得所有的读者线程总是得到一个变量的最新值,你必须将变量标记为 volatile

If you have one writer thread to modify the value of variable and multiple reader threads to read the value of variable, volatile modifier guarantees memory consistency.

如果您有一个写入器线程来修改变量的值,而多个读取器线程来读取变量的值,则 volatile 修饰符可以保证内存一致性。

If you have multiple threads to write and read variables, volatilemodifier alone does not guaranty memory consistency. You have to synchronizethe code or use high level concurrencyconstructs like Locks, Concurrent Collections, Atomic variablesetc.

如果您有多个线程来读写变量,volatile单独使用修饰符并不能保证内存一致性。你必须synchronize在代码或使用高水平并发结构,如LocksConcurrent CollectionsAtomic variables等。

Related SE questions/articles:

相关 SE 问题/文章:

Volatile variable explanation in Java docs

Java 文档中的可变变量解释

Difference between volatile and synchronized in Java

Java中volatile和synchronized的区别

javarevisitedarticle

java重访文章

回答by Akshay

voltalie Means Keep changing value.The value of this variable will never be cached thread-locally: all reads and writes will go straight to "main memory".In other words Java compiler and Thread that do not cache value of this variable and always read it from main memory.

voltalie 意味着不断改变值。这个变量的值永远不会被线程本地缓存:所有的读取和写入都将直接进入“主内存”。换句话说,Java编译器和线程不缓存这个变量的值并且总是读取它来自主内存。