— Привіт, Аміго! Погодься, з цим 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) всі винятки, що виникли.

Ніхто не гарантує, що потік можна зупинити. Він може зупинитися лише сам.