volatile

volatile задает правила видимости и порядка для одного поля:

  • запись в volatile-поле happens-before последующего чтения этого значения;
  • компилятор и CPU не могут переупорядочить операции через эту границу вопреки JMM;
  • чтение получает актуально опубликованное значение.
final class Worker implements Runnable {
    private volatile boolean running = true;
 
    void stop() {
        running = false;
    }
 
    @Override
    public void run() {
        while (running) {
            doOneIteration();
        }
    }
}

Такой флаг подходит, только если цикл не зависает в непрерываемой блокирующей операции. Для sleep, queue или lock обычно нужен interrupt.

Чего volatile не дает

volatile int counter;
counter++; // read -> add -> write, не атомарно

Два потока могут прочитать одинаковое значение и потерять одно увеличение. Для счетчика — AtomicInteger.incrementAndGet() или критическая секция.

volatile также не защищает инвариант нескольких полей:

volatile int lower;
volatile int upper; // lower <= upper не становится атомарным

Спецификация разрешает JVM разбивать чтение или запись не-volatile long/double на две 32-битные операции, хотя обычные современные JVM, как правило, этого не делают. Для volatile-полей эти чтения и записи гарантированно атомарны. Но атомарность одного присваивания не заменяет видимость, порядок и атомарность ++.

Безопасная публикация

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

См. Atomic типы данных, Race condition, Java Memory Model.

На собеседовании

  • Атомарен ли volatileCounter++? Нет.
  • Что дает volatile? Видимость и ordering/happens-before для чтения и записи поля.
  • Достаточно ли volatile для связанного состояния двух полей? Нет.
  • Атомарны ли обычные long/double по JLS? Для не-volatile спецификация допускает разделение; volatile-вариант гарантированно атомарен.
  • Можно ли volatile-флагом остановить блокирующий take()? Нет, поток должен проснуться; обычно interrupt.