Поток получает interrupt во время блокирующего ожидания: какое состояние и поведение следует ожидать после этого?
Interrupt — это запрос на прерывание, а не принудительная остановка потока. Если поток находится в методе, поддерживающем прерывание, такой метод обычно завершает ожидание с помощью InterruptedException, а флаг прерывания сбрасывается. Если поток не выполняет прерываемое ожидание, его флаг устанавливается, и поток должен сам проверить его и завершить работу корректно.
Java использует кооперативную модель остановки потоков: сам поток решает, как безопасно завершить работу после получения запроса. Такой подход нужен, чтобы не прерывать поток в произвольной точке, когда он удерживает блокировку, изменяет структуру данных или находится между связанными операциями.
Метод Thread.stop() не подходит для безопасного управления жизненным циклом потока: принудительное прекращение может оставить общие данные в несогласованном состоянии. Interrupt решает другую задачу — доставляет потоку сигнал, который код должен обработать.
Поток может долго ждать элемент очереди, завершение другого потока, таймер или доступ к блокировке. Без механизма отмены такой поток может не завершиться при остановке приложения или продолжить бесполезную работу после того, как результат стал не нужен.
Неверно считать, что вызов interrupt() немедленно убивает поток. Также опасно бездумно проглатывать InterruptedException: тогда внешний код теряет информацию о запросе отмены, а пул потоков или вызывающий компонент может продолжить ожидание недоступной задачи.
Если поток заблокирован в Thread.sleep(), Object.wait(), Thread.join() или во многих прерываемых методах java.util.concurrent, вызов interrupt() обычно приводит к InterruptedException. Для этих методов состояние прерывания очищается при выбросе исключения, поэтому обработчик должен либо передать исключение выше, либо восстановить флаг вызовом Thread.currentThread().interrupt().
Если поток выполняет обычные вычисления и не находится в прерываемом ожидании, interrupt() только устанавливает его флаг. Код должен периодически проверять Thread.currentThread().isInterrupted() или использовать метод, поддерживающий прерывание. Проверка Thread.interrupted() отличается тем, что возвращает состояние и одновременно очищает его.
Поведение зависит от API. Например, LockSupport.park() не выбрасывает InterruptedException: он возвращает управление, а флаг прерывания остаётся установленным. Поэтому после park() условие ожидания всё равно нужно проверять в цикле. Некоторые операции ввода-вывода или внешние библиотеки могут не реагировать на interrupt так, как стандартные блокирующие методы.
Для задачи в ExecutorService вызов Future.cancel(true) лишь пытается прервать выполняющий её поток. Гарантии завершения нет: задача обязана реагировать на interrupt, а операции, которые его игнорируют, могут продолжить выполнение. Освобождение ресурсов должно находиться в finally, независимо от причины завершения.
В примере цикл прекращается по флагу, а блокирующая операция может завершить ожидание исключением. Восстановление флага в обработчике сохраняет информацию для вызывающего кода или инфраструктуры.
Сервис обрабатывает задания из очереди. При остановке приложения рабочие потоки должны прекратить ожидание новых заданий и завершить уже выполняемую операцию в безопасной точке.
Принудительное завершение потока опасно: оно может прервать обновление данных или оставить ресурс незакрытым. Простое ожидание естественного завершения ненадёжно, если очередь пуста или задача зависла на поддерживающей прерывание операции.
Вариант с флагом отмены подходит для вычислительного цикла, но сам по себе не будит поток, заблокированный на очереди. Вариант с Future.cancel(true) позволяет инициировать прерывание конкретной задачи, однако требует, чтобы задача и используемые ею операции корректно обрабатывали interrupt. Выбранное решение — комбинировать отмену задачи с обработкой InterruptedException, проверкой флага в вычислительных циклах и освобождением ресурсов в finally.
В результате ожидающие потоки просыпаются, не начинают новые операции и завершаются предсказуемо. При этом система не получает ложной гарантии: если сторонняя блокирующая операция игнорирует interrupt, для неё нужен отдельный механизм ограничения времени или закрытия ресурса.
1. Почему после InterruptedException обычно нужно восстанавливать флаг прерывания?
Потому что методы вроде sleep(), wait() и join() очищают флаг перед выбросом исключения. Если обработчик просто проглотит исключение, последующий код и инфраструктура не узнают, что поток просили остановиться. Восстановление флага возвращает эту информацию на уровень, который может принять решение о завершении.
2. Чем отличаются isInterrupted() и Thread.interrupted()?
isInterrupted() проверяет флаг конкретного потока и не изменяет его. Статический Thread.interrupted() проверяет флаг текущего потока и очищает его. Поэтому второй метод следует применять осознанно: случайный вызов может скрыть сигнал отмены от последующей логики.
3. Гарантирует ли interrupt освобождение блокировки или завершение произвольной операции?
Нет. Interrupt не отнимает монитор, не откатывает изменения и не прерывает любой Java-код автоматически. Он действует только там, где текущий API или код явно поддерживает этот механизм; иначе поток должен сам проверять флаг, а для зависших внешних операций может потребоваться закрытие ресурса или тайм-аут.