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

Что такое starvation?

1.8 Middle🔥 141 комментариев
#SOLID и паттерны проектирования#Spring Boot и Spring Data

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

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

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

Thread Starvation (голодание потока)

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

Типы голодания

1. CPU Starvation (голодание по процессору)

Это когда потокам не хватает времени CPU из-за других более "активных" потоков:

public class CPUStarvationExample {
    static class HighPriorityThread extends Thread {
        public HighPriorityThread() {
            // Высокий приоритет
            setPriority(Thread.MAX_PRIORITY);
        }
        
        @Override
        public void run() {
            // Этот поток будет почти всегда занимать CPU
            while (true) {
                System.out.println("High priority thread running");
            }
        }
    }
    
    static class LowPriorityThread extends Thread {
        public LowPriorityThread() {
            // Низкий приоритет
            setPriority(Thread.MIN_PRIORITY);
        }
        
        @Override
        public void run() {
            // Этот поток может никогда не выполниться полностью
            for (int i = 0; i < 10; i++) {
                System.out.println("Low priority thread: " + i);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            }
        }
    }
    
    public static void main(String[] args) {
        new HighPriorityThread().start();
        new LowPriorityThread().start();
        
        // Низкоприоритетный поток может голодать!
    }
}

2. Lock Starvation (голодание при блокировке)

Это когда потоки не могут захватить блокировку, потому что другие потоки её постоянно держат:

public class LockStarvationExample {
    private Object lock = new Object();
    
    // Потоки, которые быстро захватывают и отпускают блокировку
    public void fastOperation() {
        synchronized (lock) {
            System.out.println("Fast operation");
        }
    }
    
    // Поток, который долго держит блокировку
    public void slowOperation() {
        synchronized (lock) {
            System.out.println("Slow operation started");
            try {
                Thread.sleep(5000); // Долгая операция
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
            System.out.println("Slow operation finished");
        }
    }
    
    public static void main(String[] args) {
        LockStarvationExample example = new LockStarvationExample();
        
        // Запустим долгую операцию
        new Thread(example::slowOperation).start();
        
        // Попытаемся выполнить быстрые операции
        for (int i = 0; i < 5; i++) {
            new Thread(example::fastOperation).start();
        }
        
        // Быстрые потоки будут ждать в очереди
    }
}

3. Reader-Writer Lock Starvation

Это когда писатели (writers) блокируют читателей (readers):

public class ReaderWriterStarvationExample {
    private ReadWriteLock rwLock = new ReentrantReadWriteLock();
    private int data = 0;
    
    // Читатель
    public int read() {
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " reading");
            return data;
        } finally {
            rwLock.readLock().unlock();
        }
    }
    
    // Писатель (может блокировать читателей)
    public void write(int value) {
        rwLock.writeLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + " writing");
            Thread.sleep(1000); // Долгая запись
            data = value;
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

Как избежать голодания?

1. Fair Locks (справедливые блокировки)

Использование fair режима в ReentrantLock:

public class FairLockExample {
    // Справедливая блокировка - потоки получают доступ в порядке очереди
    private Lock fairLock = new ReentrantLock(true);
    
    public void criticalSection() {
        fairLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + " executing");
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            fairLock.unlock();
        }
    }
    
    public static void main(String[] args) {
        FairLockExample example = new FairLockExample();
        
        // Все потоки будут обслуживаться справедливо
        for (int i = 0; i < 5; i++) {
            new Thread(example::criticalSection, "Thread-" + i).start();
        }
    }
}

2. Избегание приоритетов потоков

public class AvoidPriorityExample {
    // Хороший вариант - все потоки с одинаковым приоритетом
    public static void main(String[] args) {
        // Не устанавливайте разные приоритеты потокам
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("Thread 1: " + i);
            }
        });
        
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println("Thread 2: " + i);
            }
        });
        
        // Используй одинаковый приоритет (по умолчанию NORM_PRIORITY)
        t1.start();
        t2.start();
    }
}

3. Использование ExecutorService с ограничениями

public class ExecutorServiceExample {
    public static void main(String[] args) {
        // Используй ExecutorService с контролем
        ExecutorService executor = Executors.newFixedThreadPool(4);
        
        // Потоки в пуле справедливо распределяют ресурсы
        for (int i = 0; i < 10; i++) {
            final int taskId = i;
            executor.submit(() -> {
                System.out.println("Task " + taskId + " executing");
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
            });
        }
        
        executor.shutdown();
    }
}

4. Избегание долгих операций в критических секциях

public class ShortCriticalSectionExample {
    private Lock lock = new ReentrantLock();
    private List<String> data = new ArrayList<>();
    
    // Плохо - долгая операция в критической секции
    public void badExample() {
        lock.lock();
        try {
            // Долгая операция - другие потоки голодают
            processLargeDataset();
            data.add("result");
        } finally {
            lock.unlock();
        }
    }
    
    // Хорошо - минимум операций в критической секции
    public void goodExample() {
        // Долгая операция ВНЕ критической секции
        String result = processLargeDataset();
        
        lock.lock();
        try {
            // Только необходимая операция в критической секции
            data.add(result);
        } finally {
            lock.unlock();
        }
    }
    
    private String processLargeDataset() {
        // Долгая операция
        return "processed";
    }
}

Признаки голодания в приложении

  • Потоки не завершают работу, хотя дали им время
  • Мониторинг показывает, что некоторые потоки никогда не захватывают блокировку
  • Thread dump показывает потоки в состоянии WAITING/BLOCKED
  • Неравномерное использование CPU

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

Что такое starvation? | PrepBro