Как остановить поток в Java для работы с объектами синхронизации
Что проверяют
Знаете ли вы, что поток в Java нельзя остановить снаружи силой, а можно только попросить его завершиться. Главная проверка: как ведёт себя прерывание, когда поток ждёт на wait(), блокировке или очереди. Следом обычно спрашивают, что делать с InterruptedException.
Как отвечать
Остановка в Java кооперативная. Thread.stop() помечен устаревшим ещё в Java 1.2, потому что отпускал мониторы посреди операции и оставлял объекты в сломанном состоянии, а с Java 20 просто бросает UnsupportedOperationException. Рабочий механизм это interrupt():
- флаг прерывания:
thread.interrupt()выставляет флаг, код потока проверяет его в цикле черезThread.currentThread().isInterrupted(); - прерываемые ожидания:
sleep(),wait(),join(),BlockingQueue.take(),Condition.await()бросаютInterruptedExceptionи при этом сбрасывают флаг; - непрерываемые ожидания: вход в
synchronizedблок прерыванием не снимается, поток так и ждёт монитор. Если это важно, беритеReentrantLock.lockInterruptibly()илиtryLockс таймаутом; - обработка исключения: либо пробросить
InterruptedExceptionвыше, либо восстановить флаг вызовомThread.currentThread().interrupt()и выйти из цикла; - освобождение ресурсов:
unlock()и закрытие ресурсов вfinally, чтобы прерванный поток не оставил блокировку захваченной.
Флаг volatile boolean тоже работает, но только пока код не спит в блокирующем вызове, поэтому его совмещают с interrupt().
В прикладном коде потоки редко создают руками: у ExecutorService есть shutdown(), shutdownNow() (прерывает выполняющиеся задачи) и awaitTermination(), у отдельной задачи Future.cancel(true).
Где спотыкаются
Заводят флаг running и восстанавливают прерывание в catch, но цикл проверяет только running. Поток не выходит, следующий sleep() сразу бросает исключение снова, и получается холостой цикл.
Пишут пустой catch (InterruptedException e) {}. Сигнал об отмене теряется, и вызывающий код о нём уже не узнает.
Думают, что interrupt() мгновенно убивает поток. Он только ставит флаг, и если код его не проверяет, поток работает дальше.