Какие проблемы потокобезопасности возникнут в Spring @RestController с простым счетчиком?

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

Ответ

Да, возникнет race condition (состояние гонки), так как разделяемая переменная count не защищена от одновременного доступа.

Проблемный код:

@RestController
public class CounterController {
    private int count = 0; // Непотокобезопасное поле

    @GetMapping("/increment")
    public int increment() {
        return ++count; // Неатомарная операция: чтение-изменение-запись
    }
}

Проблемы:

  • Потерянные обновления: Два потока могут прочитать одно значение, увеличить его и записать одинаковый результат.
  • Некорректные значения: Счетчик будет показывать число меньше реального количества вызовов.

Решение 1: Использовать атомарные классы (предпочтительно)

import java.util.concurrent.atomic.AtomicInteger;

@RestController
public class CounterController {
    private final AtomicInteger count = new AtomicInteger(0);

    @GetMapping("/increment")
    public int increment() {
        return count.incrementAndGet(); // Гарантированно атомарная операция
    }
}

Решение 2: Синхронизация (менее производительно)

    @GetMapping("/increment")
    public synchronized int increment() { // Блокирует весь метод
        return ++count;
    }

Ключевой вывод: Бобы Spring по умолчанию — синглтоны. Любое изменяемое состояние в них должно быть потокобезопасным.