Что такое starvation?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
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 — это серьёзная проблема в многопоточных приложениях, которая может привести к зависаниям и неопредсказуемому поведению. Правильное проектирование и использование справедливых механизмов синхронизации помогает её избежать.