Когда следует использовать synchronized блок в Java?

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

Ответ

synchronized блок используется для обеспечения взаимного исключения (mutual exclusion) при доступе к общему ресурсу (разделяемому состоянию) из нескольких потоков. Он гарантирует, что только один поток может выполнить защищенный участок кода в данный момент.

Основные сценарии:

  1. Защита критической секции: Когда несколько потоков изменяют общие изменяемые данные.
  2. Обеспечение видимости изменений: Гарантирует, что изменения, сделанные одним потоком, будут видны другому (реализует happens-before).

Пример защиты общего счетчика:

public class ThreadSafeCounter {
    private int value = 0;
    private final Object lock = new Object(); // Специальный объект-монитор

    public void increment() {
        synchronized (lock) { // Блокировка по объекту lock
            value++; // Критическая секция защищена
        }
    }

    public int getValue() {
        synchronized (lock) { // Чтение также должно быть синхронизировано
            return value;
        }
    }
}

Преимущества synchronized блока перед synchronized методом:

  • Гибкость: Можно синхронизировать не весь метод, а только критическую секцию, уменьшая contention.
  • Контроль над монитором: Можно использовать отдельный объект (private final Object lock) вместо this, что безопаснее и предотвращает внешнюю блокировку.

Современные альтернативы: Для более сложных сценариев рассмотрите java.util.concurrent.locks.ReentrantLock (с поддержкой таймаутов, прерываний) или атомарные классы (AtomicInteger).