JavaRush /Курсы /Java Core /Остановить бушующий поток: официальная версия

Остановить бушующий поток: официальная версия

Java Core
6 уровень , 9 лекция
Открыта

— Привет, Амиго! Согласись, Элли хорошо придумала с этим Cancel?

— Ага.

— На самом деле нечто подобное существует в классе Thread. Только переменная называется не isCancel, а isInterrupted, и метод остановки, соответственно, не cancel(), а interrupt().

— Да?

— Ага. Вот смотри:

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


        while (!current.isInterrupted())
            try
                Thread.sleep(1000);
            } catch (InterruptedException e)
                e.printStackTrace();
                current.interrupt();
            }
            System.out.println("Tik");
        }
    }
}
Т.к. много нитей могут вызвать метод run одного объекта, то объект Clock в своем методе run получает объект вызвавшей его нити («текущей нити»).

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

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

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


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

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

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

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

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

— А зачем выкидывать исключение? Не лучше ли тоже просто в цикле вместо isCancel подставить isInterrupted()?

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

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

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

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

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

— Серьёзные аргументы.

— И, наконец, последнее: ты можешь в своем методе run вызывать чужой код, к которому у тебя нет доступа (исходников и/или прав их менять). Он может не иметь проверок на isInterrupted, а также перехватывать с помощью try…catch(Exception e) все возникшие исключения.

Никто не гарантирует, что нить можно остановить. Она может остановиться только сама.

Комментарии (327)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
nastya_zhadan Уровень 60
8 сентября 2025
Копирую картинку из популярных комментов
Anonymous #3585174 Уровень 33
22 августа 2025
Like
Скиталец Уровень 51
29 июля 2025
1) Метод sleep() ПОСТОЯННО проверяет был ли прерван поток, а не только вначале выполнения этого метода. 2) Метод sleep(), если был прерван поток, выбросит исключение Interrupted Exception. Это исключение, как только будет выброшено, сразу же установит флаг interrupted обратно на false. Поэтому нужно вызывать в блоке catch снова вызов метода interrupt(). Сам по себе, метод interrupt не выбрасывает исключений, он просто установит флаг interrupted в true. Затем дальше по циклу снова идет проверка условия, и вот здесь уже этот цикл прерывается. Я этого не понимал раньше, из-за этого куча проблем было с попыткой врубиться в потоки.
Den Winchester Уровень 28
9 ноября 2025
спасибо, очень помог
Anonymous #463526 Уровень 1
18 апреля 2025
опять форматирование портит все ((

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

  Thread.sleep(10000);
  clockThread.interrupt();
}
Ромушка Роман Уровень 32
20 октября 2024
По поводу прерывания я понял, это не так сложно. Не понял лишь, как быть в итоге с длинными по времени операциями или блоком из вызовов, например, 10-ти функций?
{Java_Shark} Уровень 36
14 октября 2024
++
Alex Уровень 35 Expert
10 октября 2024
Вот как я понял продолжение))

public static void main(String[] args) throws Exception {
   Clock clock = new Clock();
   Thread clockThread = new Thread(clock);
   clockThread.start();
   Thread.sleep(10000);
}
clockThread.interrupt() устанавливает флаг прерывания у потока clockThread, что приводит к тому, что метод isInterrupted() вернет true в цикле while в методе run(). Поток clockThread получит сигнал о прерывании. Если этот поток находится в состоянии ожидания (например, исполняется метод Thread.sleep()), он выбросит InterruptedException. Внутри метода run() класса Clock выполнится блок catch и поток завершит свою работу, так как isInterrupted() вернет true.
Alex Уровень 35 Expert
10 октября 2024
Вот как я понял. Метод interrupt() устанавливает внутренний флаг прерывания потока в true, а метод isInterrupted() проверяет этот флаг. Флаг прерывания — это внутреннее состояние потока, которое указывает, был ли поток прерван. Это состояние проверяется с помощью метода isInterrupted(). Метод isInterrupted() возвращает true, если поток был прерван, и false, если нет.

class Clock implements Runnable {
   public void run() {
      Thread current = Thread.currentThread(); //Метод Thread.currentThread() возвращает ссылку на объект Thread, представляющий текущий выполняющийся поток. Вызов этого метода внутри run() возвращает поток, который выполняет этот метод run(). Поток/объект Thread записывается в переменную current, которая будет хранить ссылку на текущий поток. Это даст возможность позже использовать эту переменную для проверки состояния потока (например, прерван ли он). 
      while (!current.isInterrupted()) { //если флаг будет выставлен (в true) из main во время выполнения ожидания в цикле while, то isInterrupted() вернет true и выбросится исключение InterruptedException.
         try {
            Thread.sleep(1000);
         } catch (InterruptedException e) { //при выбрасывании InterruptedException флаг прерывания сбрасывается на false.
            e.printStackTrace();
            current.interrupt(); //в блоке catch вызов current.interrupt() используется для повторного установки флага прерывания потока (в true). Например, если он был прерван во время выполнения метода sleep(). Это может быть полезно, если поток уже был прерван, и нужно убедиться, что он правильно завершает свою работу или выполняет дополнительные действия при прерывании. Таким образом, вызов current.interrupt() в блоке catch восстанавливает этот флаг (в true), что позволяет другим частям кода в потоке (например, в цикле while) корректно реагировать на прерывание.
         }
         System.out.println("Tik");
      }
   }
}
Alex Уровень 35 Expert
9 октября 2024
Ооооочень плохо разжевано... сижу уже битый час с чатомГПТ выясняя, что за текущий поток и кто кого вызывает и что проверяет. Тут либо самим нужно дать больше информации, либо ссылку на статью дать.
Kaz Уровень 32
20 августа 2024
Эта структура второй части диалога меня жестко запутала и пришлось долго распутываться пытаясь связать sleep, run и разное другое. Понятнее было бы разделить все это: - В методе Sleep есть проверка переменной isInterrupt. Она срабатывает в начале запуска sleep и если проверка не проходит, выбрасывается исключение. Метод sleep любят использовать для организации вечных циклов. - А зачем выбрасывать исключение? И не проще ли в самом цикле использовать эту проверку isInterrupt? - Ну во-первых, обработка исключения - это полезная вещь. Ты можешь сам решить, как вести себя данному потоку, если во время попытки усыпить его, выяснилось, что другой поток прервал его. Во-вторых, это актуально не только для циклов - это актуально для всех методов ВНУТРИ которых используется sleep. В идеале было бы, конечно, чтобы проверка isInterrupted происходила постоянно и перед каждым методом, но это невозможно, поэтому в JAVA придумали эту механику как дополнительный и экологичный способ дополнительной проверки. - Почему экологичный? - Представим работающий поток. Он занимает некоторые ресурсы пока открыт. И тут мы собираемся его усыпить - освободить ресурсы на время. В это время, мы делаем одну небольшую дополнительную проверку isInterrupted - да, она займет совершенно незначительные ресурсы, но даст нам много бонусов - ведь в случае, если сон и вся дальнейшая работа потока уже неактуальна, мы можем освободить ресурсы от потока полностью.