← Назад к вопросам

Что такое Thread Starvation?

3.0 Senior🔥 111 комментариев
#Многопоточность

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Thread Starvation

Thread Starvation (голодание потока) — это ситуация, когда один или несколько потоков не получают достаточно CPU времени для выполнения своей работы, потому что другие потоки постоянно захватывают ресурсы и не освобождают им время на выполнение.

Основные причины Thread Starvation

  1. Высокий приоритет у других потоков — потоки с высоким приоритетом могут вытеснять потоки с низким приоритетом
  2. Занятое ожидание (Busy Waiting) — поток занимает CPU в цикле ожидания
  3. Неправильная синхронизация — потоки ждут друг друга в deadlock-подобных ситуациях
  4. Ограниченные ресурсы — недостаточно потоков в пуле для обработки всех задач

Пример 1: Голодание из-за приоритета

public class ThreadStarvationExample {
    public static void main(String[] args) {
        // Высокоприоритетный поток
        Thread highPriorityThread = new Thread(() -> {
            while (true) {
                System.out.println("High priority thread running");
                // Этот поток постоянно занимает CPU
            }
        });
        
        // Низкоприоритетный поток - может никогда не выполниться
        Thread lowPriorityThread = new Thread(() -> {
            System.out.println("Low priority thread - может никогда не вывести это");
        });
        
        highPriorityThread.setPriority(Thread.MAX_PRIORITY);
        lowPriorityThread.setPriority(Thread.MIN_PRIORITY);
        
        highPriorityThread.start();
        lowPriorityThread.start();
    }
}

Пример 2: Голодание из-за занятого ожидания

public class BusyWaitingExample {
    private static volatile boolean flag = false;
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            // Занятое ожидание - поток постоянно проверяет переменную
            while (!flag) {
                // Busy waiting - тратит CPU впустую
                Thread.yield();
            }
            System.out.println("Flag is true!");
        });
        
        Thread thread2 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                flag = true;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        thread1.start();
        thread2.start();
        
        thread1.join();
        thread2.join();
    }
}

Правильное решение: Использование условных переменных

public class ProperWaitingExample {
    private static final Object lock = new Object();
    private static volatile boolean flag = false;
    
    public static void main(String[] args) throws InterruptedException {
        Thread thread1 = new Thread(() -> {
            synchronized (lock) {
                try {
                    // Поток ждёт, а не пытается активно проверить состояние
                    while (!flag) {
                        lock.wait(); // Отпускает lock и ждёт уведомления
                    }
                    System.out.println("Flag is true!");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        Thread thread2 = new Thread(() -> {
            synchronized (lock) {
                try {
                    Thread.sleep(1000);
                    flag = true;
                    lock.notifyAll(); // Уведомляет ожидающие потоки
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        
        thread1.start();
        thread2.start();
        
        thread1.join();
        thread2.join();
    }
}

Пример 3: Голодание в пуле потоков

import java.util.concurrent.*;

public class ThreadPoolStarvationExample {
    public static void main(String[] args) {
        // Создание пула с только одним потоком
        ExecutorService executor = Executors.newFixedThreadPool(1);
        
        // Задача 1 - берёт единственный поток
        executor.submit(() -> {
            try {
                Thread.sleep(10000); // Длительная операция
                System.out.println("Task 1 complete");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        
        // Задача 2 - будет голодать, ожидая освобождения потока
        executor.submit(() -> {
            System.out.println("Task 2 - может долго ждать!");
        });
        
        executor.shutdown();
    }
}

Правильное решение: Оптимальный размер пула

import java.util.concurrent.*;

public class OptimalThreadPoolExample {
    public static void main(String[] args) {
        // Оптимальный размер: количество ядер + 1 для I/O операций
        int coreCount = Runtime.getRuntime().availableProcessors();
        ExecutorService executor = Executors.newFixedThreadPool(coreCount + 1);
        
        for (int i = 0; i < 100; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " executed");
            });
        }
        
        executor.shutdown();
    }
}

Как избежать Thread Starvation

  1. Избегай занятого ожидания — используй wait(), notify(), Lock, Condition
  2. Будь осторожен с приоритетами — не устанавливай крайние значения приоритетов
  3. Оптимизируй размер пула потоков — используй Runtime.getRuntime().availableProcessors()
  4. Избегай блокирующих операций — используй асинхронные операции, фьючеры
  5. Используй высокоуровневые инструментыCountDownLatch, CyclicBarrier, Phaser
  6. Профилируй приложение — отслеживай использование потоков

Thread Starvation — критическая проблема в многопоточных приложениях, которая может привести к падению производительности и зависанию приложения.