ПрограммированиеJava middle/backend разработчик

Что вы знаете о работе с потоками в Java и как организовать потокобезопасность?

Проходите собеседования с ИИ помощником Hintsage

Ответ.

В Java работа с потоками реализуется через классы Thread, Runnable и из пакета java.util.concurrent. Для организации потокобезопасности используются различные механизмы синхронизации:

  • Синхронизированные блоки/методы (synchronized) позволяют нескольким потокам обращаться к общим данным без гонок.
  • volatile гарантирует видимость изменений переменной между потоками.
  • Классы из java.util.concurrent (например, ReentrantLock, Semaphore, AtomicInteger, ConcurrentHashMap) обеспечивают более гибкие способы синхронизации.

Пример синхронизации:

public class Counter { private int count = 0; public synchronized void increment() { count++; } public int getCount() { return count; } }

Пример с использованием атомарных классов:

import java.util.concurrent.atomic.AtomicInteger; public class AtomicCounter { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } }

Вопрос с подвохом.

Гарантирует ли ключевое слово volatile потокобезопасность при инкременте счётчика типа int?

Ответ: Нет. volatile обеспечивает только видимость значения между потоками, но не атомарность операций. Инкремент — не атомарная операция (count++ состоит из чтения, увеличения и записи), поэтому возможны потери данных. Для потокобезопасного инкремента нужны синхронизация или классы вроде AtomicInteger.

volatile int count = 0; // count++ не потокобезопасно!

Примеры реальных ошибок из-за незнания тонкостей темы.


История

В онлайн-магазине бонусный счёт обновлялся через volatile-счётчик. Под нагрузкой тысячи пользователей заказывали товары, и часть бонусов терялась по причине гонки потоков на count++.


История

Сотрудник использовал обычный ArrayList в качестве общего буфера между продюсером и консьюмером в многопоточном приложении, в результате возникали ConcurrentModificationException. Решением стало использование synchronized-блоков или замена на CopyOnWriteArrayList.


История

В обработке платежей разработчик делал операцию "проверить и снять сумму" через два несвязанных метода. В условиях высокой нагрузки это приводило к двойному списанию, пока не внедрили атомарные транзакции и блокировки.