Java – Как обрабатывать InterruptedException

В этом уроке мы изучим InterruptedException и когда оно выбрасывается на примере. Мы также увидим, как мы можем обрабатывать InterruptedException в нашем коде при работе в многопоточном приложении.

1. Java InterruptedException

1.1 Что такое прерывания?

В параллелизме прерывание — это сигнал потоку остановить себя и выяснить, что делать дальше. Обычно он просит поток завершить себя изящно.

Исключение InterruptedException возникает, когда поток ожидает, спит или иным образом занят, а другой поток прерывает его с помощью метода interrupt(), представленного в классе Thread.

Внутри механизм прерывания реализован с использованием внутреннего флага, известного как статус прерывания. Когда поток вызывает метод interrupt(), этот флаг устанавливается, и мы можем проверить статус этого флага с помощью метода isInterrupted(). Другой метод, присутствующий в классе Thread, Thread.interrupted(), может снова очистить этот флаг, чтобы ответить на другие запросы прерывания.

Обратите внимание, что класс Thread содержит следующие методы, используемые для создания и проверки прерываний:

  • void interrupt(): прерывает поток
  • boolean isInterrupted(): проверяет, был ли прерван текущий поток или нет. Прерванный статус потока не затрагивается этим методом.
  • boolean interrupted(): проверяет, был ли прерван текущий поток. Прерванный статус потока очищается этим методом.

1.2 Метод Thread.interrupt()

Метод interrupt() прерывает поток, в котором он вызван.

  • Если этот поток заблокирован при вызове методов sleep(), wait() или join(), то его статус прерывания будет очищен, и он получит InterruptedException.
  • Если этот поток заблокирован в операции ввода-вывода на InterruptibleChannel, то канал будет закрыт, будет установлен статус прерывания потока, и поток получит ClosedByInterruptException.
  • Если этот поток заблокирован в java.nio.channels.Selector, то будет установлен статус прерывания потока, и он немедленно вернется из операции выбора.
  • Если ни одно из предыдущих условий не выполняется, то будет установлен статус прерывания этого потока, и мы можем проверить его с помощью методов isInterrupted() или interrupted().

2. Как прервать поток

Поток может прервать другой ожидающий или спящий поток, используя метод interrupt() класса Thread.

В следующем примере TestThread работает периодически каждые 2 секунды. Он проверяет, был ли он прерван или нет. Если нет, он продолжает работу, завершает обработку и возвращается. Если он прерывается между этим другим потоком, он может завершиться корректно, выдав InterruptedException вызывающему потоку.

public class TestThread extends Thread {public void run() {try{while(true) {// Check if it is interrupted, if so then throw InterruptedExceptionif(Thread.interrupted()) {throw new InterruptedException();}// else continue workingelse {System.out.println("Continue working");}Thread.sleep(2000L);}} catch(InterruptedException e) {// Handling InterruptedException and Graceful shutdown of the ThreadSystem.out.println("Graceful shutdown");}}}

Другой поток(например, основной поток), который его запустил, иногда позже решает, что задача, выполняемая TestThread, больше не нужна, поэтому он прерывает ее.

// TestThread InstantiationTestThread t1 = new TestThread();// Starting TestThreadt1.start();// main Thread enters into sleeping stateThread.sleep(5000);// main Thread decided that TestThread is no longer needed, so interrupting itt1.interrupt(); 

При выполнении этой программы мы получим следующий вывод:

 Продолжайте работатьПродолжайте работатьПродолжайте работатьМягкое прекращение

3. Обработка InterruptedException

При работе в многопоточном приложении важно корректно обрабатывать InterruptedException, а потоки должны быстро реагировать на прерывания, чтобы избежать ситуации взаимоблокировки приложения.

  • Поскольку InterruptedException — это проверяемое исключение, нам придется обрабатывать его либо с помощью try-catch, либо с помощью ключевого слова throws.
  • Мы можем распространить его в стеке на вызывающий метод, используя ключевое слово throws. В этом случае вызывающий метод должен обработать это исключение.
  • Существуют некоторые сценарии, в которых выдача исключения невозможна, например, в случае метода run() интерфейса Runnable, он не позволит выдать исключение, поэтому для этих случаев мы можем использовать блок try-catch и явно обработать это исключение в самом вызывающем потоке вместо того, чтобы выдавать его.

Рассмотрим первый случай, когда прерванный поток выдает исключение InterruptedException и просит вызывающий метод обработать его вместо того, чтобы обрабатывать его самостоятельно.

// method throwing InterruptedException so that it is propagated to the caller methodpublic static void throwInterruptedException() throws InterruptedException{// Thread enters into sleeping stateThread.sleep(1000);// Thread interrupting itselfThread.currentThread().interrupt();// check if the Thread is interruptedif(Thread.interrupted()) {// Throwing InterruptedExceptionthrow new InterruptedException();}}// main methodpublic static void main(String[] args){try{// calling a method that throws InterruptedExceptionthrowInterruptedException();} catch(InterruptedException e){System.out.println("Thread interrupted by throwing the exception")}} 

Давайте теперь рассмотрим случай, когда прерванный поток сам обрабатывает InterruptedException, используя блок try-catch, вместо того чтобы передавать его вызывающему методу с помощью ключевого слова throws.

// method handling InterruptedException using try-catch blockpublic void run(){try{// Thread enters into sleeping stateThread.sleep(1000);// Thread interrupting itselfThread.currentThread().interrupt();} catch(InterruptedException e){System.out.println("Thread interrupted and handling exception by itself")}}// main methodpublic static void main(String[] args){// Creating new ThreadThread thread = new Thread();// Starting a Threadthread.start();}

Мы также можем создать пользовательское исключение(наше собственное InterruptedException), расширив класс Exception и вызвав наше пользовательское исключение из кода, если это необходимо.

// Creating Custom InterruptedExceptionpublic class CustomInterruptedException extends Exception{CustomInterruptedException(String message) {super(message);}}
// method throwing our CustomInterruptedExceptionpublic void throwCustomException() throws CustomInterruptedException{// Thread enters into sleeping stateThread.sleep(1000);// Thread interrupting itselfThread.currentThread().interrupt();// check if the Thread is interruptedif(Thread.interrupted()){// Throwing CustomInterruptedExceptionthrow new CustomInterruptedException("Thread interrupted by throwing custom exception");}}

3. Заключение

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

Прокрутить вверх