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

Что такое InterruptedException?

2.0 Middle🔥 201 комментариев
#Многопоточность

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

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

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

InterruptedException в Java

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

Проблема: как остановить поток?

В Java нет способа "убить" поток немедленно. Метод Thread.stop() deprecated, так как это приводит к deadlock и corruption данных:

// ❌ Запрещено — приводит к deadlock
Thread thread = new Thread(() -> {
    while (true) {
        // работа
    }
});
thread.start();
thread.stop();  // Опасно!

Решение: Interrupt mechanism

Java предоставляет interrupt flag для безопасного запроса на прерывание:

// ✅ Безопасно
Thread thread = new Thread(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        try {
            // Проверяем interrupt flag
            System.out.println("Working...");
            Thread.sleep(1000);  // Может выбросить InterruptedException
        } catch (InterruptedException ex) {
            // Поток прерван
            System.out.println("Thread interrupted!");
            break;  // или re-interrupt
        }
    }
});

thread.start();

// Позже, когда нужно остановить
thread.interrupt();  // Установить interrupt flag
thread.join();       // Ждать завершения

Как работает Interrupt

1. Установка флага (Requester):

Thread thread = new Thread(/* ... */);
thread.start();

// Где-то в другом потоке, когда нужно остановить:
thread.interrupt();  // Установить interrupt flag у thread

2. Проверка флага (Worker):

Runnable work = () -> {
    while (!Thread.currentThread().isInterrupted()) {
        // Если флаг установлен, выйти из цикла
        doWork();
    }
};

Thread thread = new Thread(work);
thread.start();
thread.interrupt();  // Флаг установлен

3. Blocking методы (sleep, wait, join):

Методы, которые блокируют поток, проверяют interrupt флаг:

public void run() {
    try {
        // Если флаг установлен, sleep выбросит InterruptedException
        Thread.sleep(5000);
    } catch (InterruptedException ex) {
        System.out.println("Sleep interrupted!");
    }
}

Когда выбрасывается InterruptedException

Blocking методы:

// Методы, которые выбрасывают InterruptedException
Thread.sleep(1000);           // Может быть прерван
object.wait();                // Monitor.wait
latch.await();                // CountDownLatch
queue.take();                 // BlockingQueue
semaphore.acquire();          // Semaphore
lock.lockInterruptibly();     // ReentrantLock
thread.join();                // Ждание завершения потока
channel.read(buffer);         // SocketChannel, FileChannel

Пример:

BlockingQueue<String> queue = new LinkedBlockingQueue<>();

Thread consumer = new Thread(() -> {
    try {
        while (true) {
            // Если флаг установлен, take выбросит InterruptedException
            String item = queue.take();
            System.out.println("Got: " + item);
        }
    } catch (InterruptedException ex) {
        System.out.println("Consumer interrupted");
        Thread.currentThread().interrupt();  // Восстановить флаг
    }
});

consumer.start();

// Позже остановить
consumer.interrupt();

Правильная обработка InterruptedException

Правило 1: Восстанови interrupt flag

// ❌ Плохо — флаг потеряется
try {
    Thread.sleep(1000);
} catch (InterruptedException ex) {
    System.out.println("Interrupted");
    // флаг сброшен, другие потоки не узнают об interruption
}

// ✅ Хорошо — восстановить флаг
try {
    Thread.sleep(1000);
} catch (InterruptedException ex) {
    Thread.currentThread().interrupt();  // Восстановить флаг
    System.out.println("Interrupted");
}

Правило 2: Не игнорируй InterruptedException

// ❌ Плохо — поток останется "нависшим"
try {
    Thread.sleep(1000);
} catch (InterruptedException ex) {
    // Ничего не делаем
}

// ✅ Хорошо
try {
    Thread.sleep(1000);
} catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
    return;  // Выйти из метода
}

Практические примеры

1. ExecutorService с timeout

ExecutorService executor = Executors.newSingleThreadExecutor();

Future<String> future = executor.submit(() -> {
    while (!Thread.currentThread().isInterrupted()) {
        try {
            System.out.println("Working...");
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            return "Interrupted";
        }
    }
    return "Done";
});

try {
    // Ждать 5 секунд, потом отменить
    String result = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException ex) {
    future.cancel(true);  // Отправить interrupt
} finally {
    executor.shutdown();
}

2. Custom interrupt-aware класс

public class InterruptibleTask implements Runnable {
    
    private volatile boolean running = true;
    
    @Override
    public void run() {
        try {
            while (running && !Thread.currentThread().isInterrupted()) {
                doWork();
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            cleanup();
        }
    }
    
    public void stop() {
        running = false;
    }
    
    private void doWork() throws InterruptedException {
        // Simulируем работу
        Thread.sleep(1000);
    }
    
    private void cleanup() {
        System.out.println("Cleaning up");
    }
}

3. При работе с Spring

@Service
public class BackgroundTaskService {
    
    @Async
    public void longRunningTask() {
        try {
            for (int i = 0; i < 100; i++) {
                if (Thread.currentThread().isInterrupted()) {
                    break;  // Выйти из цикла
                }
                processItem(i);
                Thread.sleep(100);
            }
        } catch (InterruptedException ex) {
            Thread.currentThread().interrupt();
            log.error("Task interrupted", ex);
        }
    }
}

Best Practices

1. Всегда обрабатывай InterruptedException

// ✅ Хорошо
try {
    queue.take();
} catch (InterruptedException ex) {
    Thread.currentThread().interrupt();
    return;  // Выйти
}

2. Используй isInterrupted() в циклах

while (!Thread.currentThread().isInterrupted()) {
    doWork();
}

3. Не используй Thread.stop()

// ❌ Запрещено
thread.stop();

// ✅ Правильно
thread.interrupt();

4. Используй ExecutorService для управления потоками

ExecutorService executor = Executors.newFixedThreadPool(5);
// executor автоматически управляет interrupt
executor.shutdownNow();  // Отправить interrupt всем потокам

InterruptedException — это фундаментальный механизм для безопасной работы с потоками в Java. Правильное использование критично для надёжных многопоточных приложений.