Нужно ли использовать `synchronized` вместе с `volatile` в Java?

«Нужно ли использовать `synchronized` вместе с `volatile` в Java?» — вопрос из категории Java Core, который задают на 10% собеседований Java Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Эти ключевые слова решают разные задачи, и их совместное использование требуется только в специфических сценариях.

Различия:

  • volatile гарантирует видимость изменений переменной для всех потоков, но не обеспечивает атомарность составных операций (например, count++).
  • synchronized обеспечивает и видимость, и атомарность (взаимное исключение), но вводит накладные расходы.

Когда использовать вместе? Если над volatile-переменной выполняется операция «проверка-действие» (check-then-act), необходима синхронизация для атомарности.

Пример:

public class Example {
    private volatile boolean flag = false;

    // Без synchronized два потока могут одновременно инвертировать флаг
    public synchronized void toggleFlag() {
        flag = !flag; // Неатомарная операция над volatile-переменной
    }

    // Простое чтение volatile переменной — synchronized не нужен
    public boolean isFlag() {
        return flag;
    }
}

Вывод: Используйте synchronized с volatile только для атомарных операций над volatile-полем. Для простого чтения/записи достаточно volatile.