Является ли операция инкремента (i++) атомарной в Java?

Ответ

Нет. Операция инкремента (i++) в Java не является атомарной. Она выполняется в три этапа:

  1. Чтение текущего значения переменной.
  2. Увеличение этого значения на единицу.
  3. Запись нового значения обратно в переменную.

В многопоточной среде между этими этапами может вмешаться другой поток, что приводит к состоянию гонки (race condition) и потере обновлений.

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

private int counter = 0;

public void unsafeIncrement() {
    counter++; // Не атомарно!
}

Решение: использование атомарных классов Для атомарных операций с числами используйте java.util.concurrent.atomic, например, AtomicInteger:

private AtomicInteger counter = new AtomicInteger(0);

public void safeIncrement() {
    counter.incrementAndGet(); // Гарантированно атомарная операция
}

Альтернативой является использование синхронизации (synchronized) или volatile, но AtomicInteger обычно эффективнее для данной задачи.

Ответ 18+ 🔞

А, слушай, про инкремент в Java! Ну, тут история, блядь, как с Герасимом и Муму — вроде простое действие, а в итоге пиздец и трагедия.

Так вот, эта операция i++ — она, сука, не атомарная. Вообще. Это как сказать «принеси мне пива» — вроде одна команда, а на деле три шага: встать, дойти до холодильника, вернуться. Так и тут, блядь:

  1. Прочитать, что там сейчас в переменной лежит.
  2. Прибавить к этому единичку.
  3. Записать новое значение обратно.

И представь, два потока — два таких вот Герасима-максималиста — одновременно полезли к одной переменной. Один прочитал, скажем, 5. Только он собрался прибавить, как второй, сука, уже тоже прочитал эту же пятёрку! Первый прибавил, записал 6. Второй, тупой, тоже прибавил к своей старой пятёрке, и тоже записал 6. И где, блядь, наше обновление? Съебало! Потерялось, как совесть у того же Герасима после утопления собаки. Это и есть состояние гонки, race condition, ёпта. Все бегут, а результат — хуй с горы.

Вот, смотри, как выглядит этот пиздец в коде:

private int counter = 0;

public void unsafeIncrement() {
    counter++; // Вот эта строчка — ловушка, блядь! Не атомарно!
}

Запустишь такое в нескольких потоках — и получишь в итоге число меньше, чем должно быть. Овердохуища обновлений просто испарятся.

Что делать-то, блядь? Не топить же проблему, как Муму!

Есть, сука, отличные ребята из пакета java.util.concurrent.atomic. Например, AtomicInteger. Это как тот же Герасим, но немой, сильный и дисциплинированный — делает всё за один чёткий удар. У него операция incrementAndGet() — это уже реально атомарно, в рот меня чих-пых! Никто не влезет посередине.

private AtomicInteger counter = new AtomicInteger(0);

public void safeIncrement() {
    counter.incrementAndGet(); // Вот теперь — да, блядь! Гарантированно атомарно.
}

Можно, конечно, обвешать всё synchronized или volatile наклеить, но это часто как молотком гвоздь в микроскоп забивать. AtomicInteger для такой задачи — самое оно, эффективно и правильно. Не надо изобретать велосипед, когда есть готовый, блядь, атомный таран.