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 будет исключен.
Этот эффект используется для получения простой и дешевой адаптации программы к многопоточной среде без использования сложных и ошибкоопасных техник
блокировок и
синхронизаций.