Что такое потокобезопасный класс в Java?

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

Ответ

Потокобезопасный класс — это класс, внутреннее состояние которого остаётся консистентным и корректным при одновременном использовании из нескольких потоков без необходимости внешней синхронизации со стороны клиентского кода.

Ключевой признак: Инварианты класса не нарушаются в условиях конкурентного доступа (чтения и записи).

Пример НЕпотокобезопасного и потокобезопасного класса:

// НЕ потокобезопасный счётчик
class UnsafeCounter {
    private int value = 0;
    public void increment() { value++; } // Операция не атомарна!
    public int getValue() { return value; }
}

// Потокобезопасный счётчик с использованием synchronized
class SynchronizedCounter {
    private int value = 0;
    public synchronized void increment() { value++; } // Блокировка по экземпляру
    public synchronized int getValue() { return value; }
}

// Потокобезопасный счётчик с использованием AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
class AtomicCounter {
    private AtomicInteger value = new AtomicInteger(0);
    public void increment() { value.incrementAndGet(); } // Атомарная операция CAS
    public int getValue() { return value.get(); }
}

Основные стратегии обеспечения потокобезопасности:

  1. Полная инкапсуляция состояния и использование синхронизации (synchronized методы/блоки, ReentrantLock).
  2. Использование неизменяемых (immutable) объектов. Если состояние объекта не может измениться после создания, он по определению потокобезопасен.
  3. Использование потокобезопасных классов из java.util.concurrent:
    • AtomicInteger, AtomicReference (атомарные операции).
    • ConcurrentHashMap, CopyOnWriteArrayList (специализированные коллекции).
    • ThreadLocal (хранение состояния для каждого потока отдельно).
  4. Правильная публикация объектов (использование final полей, volatile переменных).

Важно: Потокобезопасность — это свойство всего класса, а не отдельных методов. Даже если каждый метод синхронизирован, композиция вызовов (например, «проверить-и-действовать») без внешней синхронизации может привести к состоянию гонки (race condition).