Переменные volatile

Переменные volatile

При старте потока для него создаётся свой стек вызовов. Сколько запущенных потоков, столько и стеков. Каждый поток, из соображений производительности, копирует переменные из основной памяти в свой стек, получая их локальную копию. Другими словами, каждый поток кэширует себе переменные. Поток не имеет доступа к копиям переменных, которые были закэшированы другими потоками.

В такой ситуации нет никаких гарантий касательно того, когда JVM считает значение переменной из основной памяти в кэш, и когда она запишет новое значение из кэша в основную память. Т.о. значение переменной в основной памяти может отличаться от значения в кэше потока.

Когда один поток изменяет переменную, эти изменения не видны другим потокам. Эта проблема называется проблемой «видимости». Для её решения предназначено ключевое слово volatile
При объявлении переменной как volatile все потоки будут считывать и записывать её значение прямо из/в основную память.

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

Для volatile-полей запись всегда (в т.ч. long и double) является атомарной операцией. Если же volatile-переменная ссылается на другие объекты (например, массив, List или какой-нибудь ещё класс), то всегда «свежей» будет только ссылка на сам объект, но не на всё, в него входит.

Happens-before: запись в volatile-переменные происходит раньше, чем чтение из них.

Использования volatile достаточно для реализации потокобезопасности когда существует только один модифицирующий поток и несколько считывающих.

Чтение/запись в основную память более дорогостоящая операция, чем чтение/запись в кэш. Доступ к volatile-переменным предотвращает переупорядочивание инструкций, что является обычным методом повышения производительности. Т.о. volatile-переменные нужно использовать только если есть весомая необходимость обеспечить видимость переменных.
Многопоточность