Вопрос-ответ

When exactly do you use the volatile keyword in Java? [duplicate]

Когда именно вы используете ключевое слово volatile в Java?

Я прочитал "Когда использовать 'volatile' в Java?" но я все еще в замешательстве. Как я узнаю, когда мне следует пометить переменную volatile? Что, если я пойму неправильно, либо опущу volatile для чего-то, что в нем нуждается, либо добавлю volatile для чего-то, что этого не делает? Каковы эмпирические правила при определении того, какие переменные должны быть volatile в многопоточном коде?

Переведено автоматически
Ответ 1

В основном вы используете его, когда хотите разрешить доступ к переменной-члену нескольким потокам, но не нуждаетесь в составной атомарности (не уверен, что это правильная терминология).

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++;
}
}

приведенный выше плохой пример, потому что вам нужна составная атомарность.

 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++;
}
}

Теперь перейдем к действительному примеру:

 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);
}
}
}

Итак, почему вы не можете просто использовать private static int temperature? На самом деле вы можете (в том смысле, что ваша программа не взорвется или что-то в этом роде), но изменение на temperature другим потоком может быть, а может и не быть "видимым" для основного потока.

По сути, это означает, что возможно даже, что ваше app. продолжает писать Today's temperature is 0 вечно, если вы не используете volatile (на практике значение имеет тенденцию со временем становиться видимым. Однако вам не следует рисковать, не используя volatile при необходимости, поскольку это может привести к неприятным ошибкам (вызванным неполностью сконструированными объектами и т.д.).

Если вы добавите volatile ключевое слово к чему-то, что не требуется volatile, это не повлияет на корректность вашего кода (т. Е. Поведение не изменится). С точки зрения производительности, это будет зависеть от реализации JVM. Теоретически вы можете получить небольшое снижение производительности, потому что компилятор не может выполнить оптимизацию переупорядочения, придется аннулировать кэш процессора и т.д., Но опять же, компилятор может доказать, что к вашему полю никогда не смогут получить доступ несколько потоков, и полностью удалить эффект volatile ключевое слово и скомпилировать его по идентичным инструкциям.

Редактировать:
Ответ на этот комментарий:


Хорошо, но почему мы не можем синхронизировать todaysTemperature и создать синхронизированный геттер для определения температуры?


Вы можете, и оно будет вести себя корректно. Все, что вы можете сделать с помощью volatile, можно сделать с помощью synchronized, но не наоборот. Есть две причины, по которым вы могли бы предпочесть, volatile если вы можете:


  1. Меньше подвержен ошибкам: это зависит от контекста, но во многих случаях использование volatile менее подвержено ошибкам параллелизма, таким как блокировка при удержании блокировки, взаимоблокировки и т.д.

  2. Более производительный: в большинстве реализаций JVM, volatile может иметь значительно более высокую пропускную способность и лучшую задержку. Однако в большинстве приложений разница слишком мала, чтобы иметь значение.

Ответ 2

Volatile наиболее полезен в алгоритмах без блокировок. Вы помечаете переменную, содержащую общие данные, как volatile, когда вы не используете блокировку для доступа к этой переменной и хотите, чтобы изменения, внесенные одним потоком, были видны в другом потоке, или вы хотите создать отношение "происходит после", чтобы гарантировать, что вычисления не будут переупорядочены, опять же, чтобы гарантировать, что изменения станут видны в соответствующее время.

В JMM Cookbook описывается, какие операции можно упорядочивать, а какие нельзя.

Ответ 3

volatile ключевое слово гарантирует, что значение переменной volatile всегда будет считываться из основной памяти, а не из локального кэша потока.

Из учебника по параллелизму Java :


Использование volatile переменных снижает риск ошибок согласованности памяти, поскольку любая запись в volatile переменную устанавливает связь "происходит до" с последующими чтениями этой же переменной


Это означает, что изменения переменной volatile всегда видны другим потокам. Это также означает, что когда поток считывает переменную volatile , он видит не только последнее изменение переменной volatile, но и побочные эффекты кода, который привел к изменению.

Что касается вашего запроса:


Как я узнаю, когда мне следует пометить переменную volatile? Каковы практические правила при определении того, какие переменные должны быть volatile в многопоточном коде?


Если вы чувствуете, что все потоки чтения всегда получают последнее значение переменной, вы должны пометить переменную как volatile

Если у вас есть один поток записи для изменения значения переменной и несколько потоков чтения для чтения значения переменной, модификатор volatile гарантирует согласованность памяти.

Если у вас есть несколько потоков для записи и чтения переменных, volatile один только модификатор не гарантирует согласованности памяти. Вы должны synchronize доработать код или использовать высокоуровневые конструкции с параллелизмом, такие как Locks, Concurrent Collections, Atomic variables и т.д.

Вопросы по SE по теме / статьи:

Объяснение переменной Volatile в Java docs

Разница между volatile и synchronized в Java

javarevisited статья

Ответ 4

volatile Также может использоваться для безопасной публикации неизменяемых объектов в многопоточной среде.

Объявление поля типа public volatile ImmutableObject foo гарантирует, что все потоки всегда будут видеть ссылку на доступный в данный момент экземпляр.

Подробнее об этом читайте в разделе Параллелизм Java на практике.

java multithreading