Какой будет результат инкремента (`i++`) общей переменной типа `int` из нескольких потоков без синхронизации?

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

Ответ

Результат будет непредсказуемым и меньше ожидаемого итогового значения. Операция i++ не является атомарной. Она состоит из трех шагов: 1) чтение значения, 2) увеличение, 3) запись нового значения. Потоки могут перекрывать эти шаги, приводя к состоянию гонки (race condition) и потере обновлений.

Демонстрация проблемы:

public class UnsafeCounter {
    private int count = 0;
    public void increment() { count++; } // НЕБЕЗОПАСНО!
    public int getCount() { return count; }
}

// Запуск двух потоков, каждый инкрементирует счетчик 100_000 раз
// Итоговое значение, скорее всего, будет меньше 200_000.

Правильные решения:

  1. Использование AtomicInteger (наиболее предпочтительно для счетчиков):

    private AtomicInteger atomicCount = new AtomicInteger(0);
    public void safeIncrement() {
        atomicCount.incrementAndGet(); // Атомарная операция
    }
  2. Синхронизированный метод или блок:

    private int syncCount = 0;
    public synchronized void safeIncrement() { syncCount++; }
    // Или
    private final Object lock = new Object();
    public void safeIncrement() {
        synchronized (lock) { syncCount++; }
    }
  3. Использование volatile НЕ решает проблему инкремента, так как ++ — это операция «чтение-изменение-запись». volatile гарантирует только видимость изменений, но не атомарность составных операций.