В этом уроке мы изучим 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 в нашем коде.