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