Каковы риски использования ключевого слова synchronized в Java?

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

Ответ

Использование synchronized сопряжено с рисками, связанными с производительностью и корректностью многопоточного кода.

Основные риски:

  • Грубая блокировка: Синхронизация метода блокирует весь объект (this), даже если требуется защитить доступ только к одному полю.
  • Взаимная блокировка (Deadlock): Возникает, когда два или более потока блокируют разные ресурсы и ожидают освобождения ресурсов, захваченных другими.
  • Снижение производительности: Избыточная синхронизация создает contention (состязание) за монитор, что приводит к приостановке потоков и накладным расходам на переключение контекста.
  • Отсутствие гибкости: Невозможно попытаться захватить блокировку с таймаутом или прервать ожидание.

Когда использовать:

  • Для простых сценариев с короткими критическими секциями.
  • Когда альтернативы из java.util.concurrent избыточны для задачи.

Пример:

public class Counter {
    private int count = 0;

    // Безопасно, но блокирует весь экземпляр Counter
    public synchronized void increment() {
        count++;
    }
}

Альтернативы и лучшие практики:

  • Атомарные классы (AtomicInteger, LongAdder): Для одиночных операций над примитивами.
  • Явные блокировки (ReentrantLock): Предоставляют больше контроля (таймауты, прерываемость, честность).
  • Потокобезопасные коллекции (ConcurrentHashMap, CopyOnWriteArrayList): Вместо синхронизации стандартных коллекций.
  • Синхронизация по отдельному объекту-замку: Для уменьшения гранулярности блокировки.
    private final Object lock = new Object();
    public void method() {
    synchronized(lock) { // Блокируется только lock, а не весь this
        // ...
    }
    }