Вы в основном используете его, когда хотите разрешить доступ к переменной-члену нескольким потокам, но при этом не требуется составная атомарность (не уверен, что это правильная терминология).
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
? Фактически, вы можете (в том смысле, что ваша программа не взорвется или что-то в этом роде), но изменение температуры
другим потоком может быть или не быть «видимым» для основного потока.
В основном это означает, что возможно, что ваш app. продолжает писать Текущая температура равна 0
навсегда, если вы не используете volatile
(на практике значение имеет тенденцию становиться видимым со временем. Однако вы не должны рисковать не использовать volatile при необходимости, так как это может привести к неприятным ошибкам (вызванным полностью построенными объектами и т. д.).
Если вы поместите ключевое слово volatile
на то, что не требует volatile
, это не повлияет на правильность вашего кода (т.е. поведение не изменится). С точки зрения производительности, это будет зависеть от реализации JVM. Теоретически вы можете получить небольшое снижение производительности, потому что компилятор не может этого сделать. переупорядочивание оптимизаций, необходимо сделать недействительным кэш ЦП и т. д., но опять же компилятор может доказать, что к вашему полю никогда нельзя получить доступ для нескольких потоков, и полностью удалить эффект ключевого слова volatile
и скомпилировать его в идентичные инструкции.
РЕДАКТИРОВАТЬ:
Ответ на этот комментарий:
Хорошо, но почему мы не можем синхронизировать сегодняшнюю температуру и создать синхронизированный получатель температуры?
Можно, и он будет вести себя правильно. Все, что можно сделать с помощью volatile
, можно сделать с помощью synchronized
, но не наоборот. Есть две причины, по которым вы можете предпочесть volatile
, если можете:
volatile
менее подвержено ошибкам параллелизма, например, блокировка при удержании блокировки, взаимоблокировки и т. д. volatile
может иметь значительно более высокую пропускную способность и большую задержку. Однако в большинстве приложений разница слишком мала, чтобы иметь значение. http://mindprod.com/jgloss/volatile.html
«Ключевое слово volatile используется для переменных, которые могут быть изменены одновременно другими потоками»
«Так как other потоки не могут видеть локальные переменные, нет необходимости помечать локальные переменные как изменчивые. Вам нужно синхронизировать изменения для координации изменений переменных из разных потоков, но часто volatile достаточно просто посмотреть на них »
Volatile наиболее полезен в алгоритмах без блокировки. Вы помечаете переменную, содержащую общие данные, как изменчивую, когда вы не используете блокировку для доступа к этой переменной и хотите, чтобы изменения, сделанные одним потоком, были видны в другом, или вы хотите создать отношение «происходит после», чтобы гарантировать, что вычисления не переупорядочивать, опять же, чтобы изменения стали заметны в нужное время.
Поваренная книга JMM описывает, какие операции можно переупорядочить, а какие нет.
volatile
также можно использовать для безопасной публикации неизменяемых объектов в многопоточной среде.
Объявление поля типа public volatile ImmutableObject foo
гарантирует, что все потоки всегда видят доступную в данный момент ссылку на экземпляр.
См. Параллелизм Java на практике для получения дополнительной информации по этой теме.