— Привет, Амиго!

Все новое – хорошо забытое старое. Сегодня я буду рассказывать про остановку нитей. Надеюсь, ты уже забыл, как работает метод interrupt().

— Да, Элли, полностью забыл.

— Отлично. Тогда напоминаю.

В Java, если кто-то хочет остановить работающую нить, у него есть возможность подать нити об этом сигнал. Для этого нужно установить скрытую переменную нити isInterrupted в true.

У каждой нити (у класса Thread) есть метод interrupt(), который используется для установки такого флага. При вызове метода interrupt() переменная isInterrupted внутри нити устанавливается равной true.

И когда нить вызывает методы Thread.sleep() или метод join(), в этих методах происходит скрытая проверка – а не выставлен ли у нашей текущей нити флаг isInterrupted. Если этот флаг выставлен (переменная isInterrupted равно true), то методы выбрасывают исключение InterruptedException.

Вот, напомню тебе старый пример:

Код Описание
class Clock implements Runnable
{
public void run()
{
Thread current = Thread.currentThread();

while (!current.isInterrupted())
{
Thread.sleep(1000);
System.out.println("Tik");
}
}
}
Объект Clock в своем методе run получает объект текущей его нити.

Класс Clock (часы) будет писать в консоль раз в секунду слово «Tik», пока переменная isInterrupt текущей нити равна false.

Когда переменная isInterrupt станет равной true, метод run завершится.

public static void main(String[] args)
{
Clock clock = new Clock();
Thread clockThread = new Thread(clock);
clockThread.start();

Thread.sleep(10000);
clockThread.interrupt();
}
Главная нить, запускает дочернюю нить – часы, которая должна работать вечно.

Ждет 10 секунд и отменяет задание, вызовом метода interrupt.

Главная нить завершает свою работу.

Нить часов завершает свою работу.

Тут мы используем метод sleep для организации вечного цикла в методе run. В цикле есть автоматическая проверка переменной isInterrupt. Если нить вызовет метод sleep, то этот метод сначала проверит, а не установлена ли для текущей (вызвавшей его нити) переменная isInterrupt в true. И если установлена, то метод не будет спать, а выкинет исключение InterruptedException.

— Но в этом примере мы постоянно проверяем переменную isInterrupted в условии цикла.

Я помню, были какие-то причины, по которым мы не могли использовать такой подход. Не напомнишь?

— Во-первых, не всегда в методе run есть цикл. Метод может состоять просто из двух десятков вызовов других методов. Тогда перед вызовом каждого придется добавлять проверку isInterrupted.

Во-вторых, вдруг какой-то метод очень долго исполняется, т.к. делает много разных действий.

В-третьих, выкидывание исключения – это не замена проверке isInterrupted, а скорее удобное дополнение. Выкинутое исключение позволяет быстро раскрутить стек вызовов до самого run.

В-четвертых, метод sleep часто используют, и, получается, к такому полезному методу неявно добавили не менее полезную проверку. Вроде бы никто специально проверку не добавлял, а она есть. Это очень ценно, когда ты используешь много чужого кода и не можешь сам добавить в него проверку.

В-пятых, дополнительная проверка не приводит к снижению производительности. Вызов метода sleep значит, что нить должна ничего не делать (спать), поэтому дополнительная работа никому не мешает.

— Точно, именно это ты и говорила тогда.

А что насчет твоей фразы «Никто не гарантирует, что нить можно остановить. Она может остановиться только сама». Можешь ее растолковать?

— Конечно.

Раньше, в первых версиях Java, у нитей был метод stop(). И при его вызове Java-машина действительно останавливала нить. Но потом оказалось, что если нить, которую прерывали таким образом, делала что-то за пределами Java-машины и (например писала в файл или вызывала функции ОС), то прерывание такой нити приводила к большому количеству проблем – незакрытые файлы, неосвобожденные занятые системные ресурсы и т.д.

На общем совещании проектировщиков Java было решено убрать метод принудительной остановки у нитей. Теперь мы всего лишь можем установить определенный флаг (isInterrupted) и надеяться, что код нити был написан правильно, и этот флаг будет обработан. Этот флаг – это как плакат с надписью – «нить, остановись, пожалуйста, очень надо!». Но остановится она или нет – это ее дело.

— А как же InterruptedException?

— А если внутри кода, который работает в этой нити, есть куча try-catch блоков? Даже если InterruptedException где-нибудь да и выскочит, абсолютно не факт, что какой-то try-catch не захватит его и не забудет о нем. Так что никаких гарантий остановки нити нет.

Другое дело, что нити уже считают достаточно низкоуровневым программированием. Но об этом я расскажу тебе в следующий раз.

— Прямо не Элли, а Шахеризада!

— Так, Амиго! Все понятно по текущей лекции?

— Ага.

— Вот и отлично.