← Назад к вопросам
Какие знаешь проблемы при работе с многопоточностью?
2.0 Middle🔥 201 комментариев
#Многопоточность
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблемы при работе с многопоточностью
Многопоточность - мощный инструмент, но и сложный в управлении. Вот основные проблемы, с которыми я регулярно сталкиваюсь:
1. Race Condition (Состояние гонки)
Несколько потоков одновременно обращаются к одному ресурсу, и результат зависит от порядка выполнения.
// Проблема
public class Counter {
private int count = 0;
public void increment() {
count++; // Эта операция НЕ атомарна!
// 1. Прочитать count
// 2. Увеличить на 1
// 3. Записать обратно
}
}
// Два потока вызывают increment() одновременно:
// Thread 1: читает 0, записывает 1
// Thread 2: читает 0, записывает 1
// Результат: 1, вместо 2
// Решение 1: synchronized
public synchronized void increment() {
count++;
}
// Решение 2: AtomicInteger
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
count.incrementAndGet();
}
2. Deadlock (Взаимная блокировка)
Два или более потока ждут друг друга и заблокированы навечно.
// Классический deadlock
public class BankAccount {
private int balance = 0;
public synchronized void transfer(BankAccount other, int amount) {
this.balance -= amount;
other.deposit(amount); // Здесь будет блокировка!
}
public synchronized void deposit(int amount) {
this.balance += amount;
}
}
// Проблема:
// Thread 1: блокирует account1, пытается заблокировать account2
// Thread 2: блокирует account2, пытается заблокировать account1
// Deadlock!
// Решение: порядок лок-объектов
public class BankAccount {
private int balance = 0;
private static final Object LOCK = new Object();
public void transfer(BankAccount other, int amount) {
// Всегда одинаковый порядок блокировки
BankAccount first = this.balance < other.balance ? this : other;
BankAccount second = this.balance < other.balance ? other : this;
synchronized(first) {
synchronized(second) {
first.balance -= amount;
second.balance += amount;
}
}
}
}
3. Starvation (Голодание потока)
Поток не получает доступ к ресурсу и остаётся в очереди надолго.
// Проблема с приоритетами
for (int i = 0; i < 10; i++) {
new Thread(() -> {
synchronized(resource) {
doWork();
}
}, "Priority-High-" + i).start();
}
// Низкоприоритетные потоки могут никогда не выполниться
// Решение: ReentrantReadWriteLock, FairSync
ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); // fair
4. Visibility Problem (Видимость памяти)
Один поток изменяет переменную, другой не видит изменения.
// Проблема
public class Flag {
private boolean ready = false; // Может кэшироваться в register CPU
public void set() {
ready = true;
}
public void waitForReady() {
while (!ready) { // Может никогда не завершиться!
// Второй поток может видеть кэшированное значение false
}
}
}
// Решение 1: volatile
private volatile boolean ready = false;
// Решение 2: synchronized
private boolean ready = false;
public synchronized void set() {
ready = true;
}
public synchronized boolean isReady() {
return ready;
}
5. Livelock (Живая блокировка)
Потоки активны, но не совершают полезную работу.
// Пример: потоки постоянно откатывают друг друга
public void doWork() {
while (true) {
if (tryLock(resource1)) {
if (tryLock(resource2)) {
// Делаем работу
break;
} else {
unlock(resource1); // Откатываем!
}
}
}
}
// Решение: время ожидания, back-off стратегия
Thread.sleep(random.nextInt(100)); // random back-off
6. ConcurrentModificationException
Изменение коллекции во время итерации.
// Проблема
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
for (String item : list) {
if (item.equals("a")) {
list.remove(item); // ConcurrentModificationException!
}
}
// Решение
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
if (item.equals("a")) {
it.remove(); // Безопасно
}
}
7. Double-Checked Locking
Ошибка при ленивой инициализации.
// Небезопасно (даже с synchronized)
if (instance == null) { // Первая проверка
synchronized(this) {
if (instance == null) { // Вторая проверка
instance = new Singleton();
}
}
}
// Правильно
private volatile Singleton instance; // volatile!
if (instance == null) {
synchronized(this) {
if (instance == null) {
instance = new Singleton();
}
}
}
Инструменты для борьбы
- synchronized — простая синхронизация
- volatile — гарантирует видимость памяти
- AtomicInteger/AtomicReference — атомарные операции без лока
- ReentrantLock — более гибкая альтернатива synchronized
- ReadWriteLock — раздельные блокировки для чтения/письма
- CountDownLatch/Semaphore — синхронизация потоков
- ThreadLocal — данные, изолированные по потокам
Best Practices
- Минимизируй критические секции
- Избегай nested locks
- Используй высокоуровневые инструменты (Stream, CompletableFuture)
- Тестируй многопоточный код тщательно
- Используй stress-тесты и ThreadSanitizer
Многопоточность требует глубокого понимания и аккуратного проектирования. Ошибки в многопоточном коде часто проявляются редко и сложно воспроизводятся.