volatile
– ключевое слово для работы с многопоточностью. Не то же самое, что volatile
в C++, не обязано делать что-либо с кэшем процессора. Оказывает на поле объекта ровно два эффекта.Во-первых, чтение/запись такого поля становятся атомарными. Это применение актуально только для
long
и double
, и не на всех платформах. Для остальных типов полей это верно и так.Второй и самый интересный эффект – пара событий запись-чтение для такого поля являются synchronization actions. Значит, между ними существует отношение happens-before. Это значит, что существует гарантия, что произошедшее в памяти до записи будет видно после чтения. То есть будут успешно прочитаны значения, записанные в другие переменные.
Для полного понимания темы рекомендуется к просмотру доклад Алексея Шипилёва и документация. Лучше всего эффект
volatile
иллюстрирует задача из этого доклада, которую часто и дают в качестве этого вопроса. Вопрос – что выведет данный код:int a; int b;
// thread 1:
a = 1;
b = 2;
// thread 2:
System.out.print(b);
System.out.print(a);
Трюк в том, что помимо очевидных 21 (поток 2 отработал после 1), 00 (поток 2 отработал до 1, переменные еще не инициализированы) и 01 (поток 2 сработал между записями), может быть и неожиданные 20. Дело в том, что для операторов одного потока действует program order, он гарантирует хотя бы видимость правильной последовательности операций. Между потоками необходим «мост» из happens-before. Его даст применение модификатора
volatile
к переменной b
, неожиданный результат 20 будет исключен.Этот эффект используется для получения простой и дешевой адаптации программы к многопоточной среде без использования сложных и ошибкоопасных техник блокировок и синхронизаций.