Какие типичные проблемы синхронизации потоков существуют в Java?

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

Ответ

В многопоточном программировании на Java распространены следующие проблемы синхронизации:

1. Состояние гонки (Race Condition) Возникает, когда несколько потоков одновременно изменяют общие данные, и результат зависит от порядка их выполнения.

public class Counter {
    private int count = 0;
    // Без синхронизации инкременты могут "потеряться"
    public synchronized void increment() { // synchronized решает проблему
        count++;
    }
}

2. Взаимная блокировка (Deadlock) Два или более потока бесконечно ждут друг друга, удерживая нужные друг другу блокировки.

// Поток 1: synchronized(resourceA) { wait for resourceB }
// Поток 2: synchronized(resourceB) { wait for resourceA }

3. Активная блокировка (Livelock) Потоки постоянно меняют своё состояние в ответ на действия друг друга, но не могут продвинуться в выполнении полезной работы (аналог "вежливой" взаимной уступки).

4. Голодание (Starvation) Поток долго не может получить доступ к общему ресурсу, потому что другие потоки (с более высоким приоритетом или частые) постоянно его монополизируют.

Инструменты для решения:

  • Ключевое слово synchronized (методы, блоки).
  • Классы из пакета java.util.concurrent: Lock, Semaphore, CountDownLatch, CyclicBarrier.
  • Атомарные классы (AtomicInteger, AtomicReference и др.) для lock-free операций.
  • Неизменяемые (immutable) объекты — самый безопасный способ избежать синхронизации.
  • Потокобезопасные коллекции (ConcurrentHashMap, CopyOnWriteArrayList).