When exactly do you use the volatile keyword in Java? [duplicate]
Когда именно вы используете ключевое слово volatile в Java?
Я прочитал "Когда использовать 'volatile' в Java?" но я все еще в замешательстве. Как я узнаю, когда мне следует пометить переменную volatile? Что, если я пойму неправильно, либо опущу volatile для чего-то, что в нем нуждается, либо добавлю volatile для чего-то, что этого не делает? Каковы эмпирические правила при определении того, какие переменные должны быть volatile в многопоточном коде?
Переведено автоматически
Ответ 1
В основном вы используете его, когда хотите разрешить доступ к переменной-члену нескольким потокам, но не нуждаетесь в составной атомарности (не уверен, что это правильная терминология).
classBadExample { privatevolatileint counter;
publicvoidhit(){ /* 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++; } }
приведенный выше плохой пример, потому что вам нужна составная атомарность.
classBadExampleFixed { privateint counter;
publicsynchronizedvoidhit(){ /* * 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++; } }
//Called by some other thread than main publicstaticvoidtodaysTemperature(int temp){ // This operation is a single operation, so you // do not need compound atomicity temperature = temp; }
publicstaticvoidmain(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 если вы можете:
Меньше подвержен ошибкам: это зависит от контекста, но во многих случаях использование volatile менее подвержено ошибкам параллелизма, таким как блокировка при удержании блокировки, взаимоблокировки и т.д.
Более производительный: в большинстве реализаций JVM, volatile может иметь значительно более высокую пропускную способность и лучшую задержку. Однако в большинстве приложений разница слишком мала, чтобы иметь значение.
Ответ 2
Volatile наиболее полезен в алгоритмах без блокировок. Вы помечаете переменную, содержащую общие данные, как volatile, когда вы не используете блокировку для доступа к этой переменной и хотите, чтобы изменения, внесенные одним потоком, были видны в другом потоке, или вы хотите создать отношение "происходит после", чтобы гарантировать, что вычисления не будут переупорядочены, опять же, чтобы гарантировать, что изменения станут видны в соответствующее время.
В JMM Cookbook описывается, какие операции можно упорядочивать, а какие нельзя.
Ответ 3
volatileключевое слово гарантирует, что значение переменной volatile всегда будет считываться из основной памяти, а не из локального кэша потока.
Использование volatile переменных снижает риск ошибок согласованности памяти, поскольку любая запись в volatile переменную устанавливает связь "происходит до" с последующими чтениями этой же переменной
Это означает, что изменения переменной volatile всегда видны другим потокам. Это также означает, что когда поток считывает переменную volatile , он видит не только последнее изменение переменной volatile, но и побочные эффекты кода, который привел к изменению.
Что касается вашего запроса:
Как я узнаю, когда мне следует пометить переменную volatile? Каковы практические правила при определении того, какие переменные должны быть volatile в многопоточном коде?
Если вы чувствуете, что все потоки чтения всегда получают последнее значение переменной, вы должны пометить переменную как volatile
Если у вас есть один поток записи для изменения значения переменной и несколько потоков чтения для чтения значения переменной, модификатор volatile гарантирует согласованность памяти.
Если у вас есть несколько потоков для записи и чтения переменных, volatile один только модификатор не гарантирует согласованности памяти. Вы должны synchronize доработать код или использовать высокоуровневые конструкции с параллелизмом, такие как Locks, Concurrent Collections, Atomic variables и т.д.
volatile Также может использоваться для безопасной публикации неизменяемых объектов в многопоточной среде.
Объявление поля типа public volatile ImmutableObject foo гарантирует, что все потоки всегда будут видеть ссылку на доступный в данный момент экземпляр.