Java教程——如何处理InterruptedException

1,351 阅读5分钟

在本教程中,我们将 通过一个例子来学习InterruptedException以及它何时被抛出。我们还将看到在多线程应用程序中工作时,我们如何在代码中处理InterruptedException

1.Java中的InterruptedException

1.1.什么是中断?

在并发中,中断是一个信号,让Thread停止自己,并找出下一步该做什么。一般来说,它要求Thread优雅地终止自己。

当一个线程在等待、睡眠或其他情况下被占用时,另一个线程通过使用Thread类中的interrupt()方法来中断它,就会抛出InterruptedException

在内部,中断机制是通过一个称为中断状态的内部标志来实现的。当一个线程调用interrupt()方法时,这个标志就会被设置,我们可以使用isInterrupted() 方法来检查这个标志的状态。在Thread类中还有一个方法。 Thread.interrupted()可以再次清除这个标志以响应其他中断请求。

请注意,Thread 类包含以下用于创建和检查中断的方法。

  • void interrupt():中断一个线程
  • boolean isInterrupted():检查当前线程 是否被中断。 线程 的中断状态不受此方法的影响
  • boolean interrupted():检查当前线程 是否已被中断。该 线程 的中断状态被此方法清除

1.2.Thread.interrupt()方法

interrupt()方法会中断它所调用的线程。

  • 如果该线程在调用sleep()wait()join()方法时被阻塞,那么它的中断状态将被清除,并且会收到一个InterruptedException
  • 如果该线程在对一个*可中断通道的I/O操作中被阻塞,那么该通道将被关闭,该线程的中断状态将被设置,并且该线程将收到一个ClosedByInterruptException
  • 如果该线程在java.nio.channels.Selector中被阻塞,那么该线程的中断状态将被设置,并且它将立即从选择操作中返回。
  • 如果前面的条件都不成立,那么这个线程的中断状态将被设置,我们可以使用 isInterrupted()interrupted()方法来检查。

2.如何中断一个线程

一个线程 可以通过使用Thread 类的interrupt() 方法来中断另一个正在等待或睡眠的线程

在下面的例子中,TestThread每隔2秒就定期工作一次。它检查它是否被打断了。如果没有,它继续工作,完成处理并返回。如果它在中间被另一个线程打断,它可以通过向调用者线程抛出InterruptedException来优雅地终止工作。

public class TestThread extends Thread {  
 
	public void run() {  
		try{

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

另一个启动它的线程(例如:主线程)有时会决定TestThread执行的任务不再需要了,所以它中断了它。

// TestThread Instantiation
TestThread t1 = new TestThread(); 
 
// Starting TestThread
t1.start(); 

// main Thread enters into sleeping state 
Thread.sleep(5000);

// main Thread decided that TestThread is no longer needed, so interrupting it
t1.interrupt(); 

当我们执行这个程序时,我们将得到以下输出。

Continue working
Continue working
Continue working

Graceful Termination

3.处理InterruptedException

当在一个多线程的应用程序中工作时,优雅地处理InterruptedException是很重要的,线程应该及时响应中断,以避免应用程序进入死锁 状态。

  • 由于InterruptedException 是一个*被检查的异常,所以我们必须使用try-catchthrows* 关键字来处理它。
  • 我们可以通过使用throws 关键字将其在堆栈中向上传播到调用者方法。在这种情况下,调用者方法需要处理这个异常。
  • 在有些情况下,抛出异常是不可能的,比如Runnable接口的run()方法,它不允许抛出异常,所以对于这些情况,我们可以使用try-catch块,在 调用Thread本身 明确地处理这个异常,而不是抛出它。

让我们考虑第一种情况,当一个被中断的线程 抛出InterruptedException ,并要求调用者方法处理它,而不是自己处理它。

// method throwing InterruptedException so that it is propagated to the caller method
public static void throwInterruptedException() throws InterruptedException
{
     // Thread enters into sleeping state
     Thread.sleep(1000);

     // Thread interrupting itself
     Thread.currentThread().interrupt();

     // check if the Thread is interrupted
     if (Thread.interrupted()) {
        // Throwing InterruptedException
        throw new InterruptedException();
     } 
}

// main method
public static void main(String[] args)
{
    try{
             // calling a method that throws InterruptedException  
             throwInterruptedException();
         } catch(InterruptedException e){
             System.out.println("Thread interrupted by throwing the exception")
         } 
} 

现在让我们考虑这样的情况:一个被中断的线程 自己用try-catch块来处理InterruptedException,而不是用throws关键字将其传播给调用者方法。

// method handling InterruptedException using try-catch block
public void run()
{
     try{
             // Thread enters into sleeping state
             Thread.sleep(1000);
             // Thread interrupting itself
             Thread.currentThread().interrupt(); 
          } catch(InterruptedException e){
               System.out.println("Thread interrupted and handling exception by itself")
          }
}

// main method
public static void main(String[] args)
{
    // Creating new Thread
    Thread thread = new Thread();
    // Starting a Thread
     thread.start();
}

我们也可以通过扩展Exception 类来创建用户定义的异常(我们自己定制的InterruptedException),并在需要时从代码中抛出我们定制的异常。

// Creating Custom InterruptedException
public class CustomInterruptedException extends Exception 
{ 
     CustomInterruptedException(String message) { 
       super(message); 
     } 
}
// method throwing our CustomInterruptedException 
public void throwCustomException() throws CustomInterruptedException
{
    // Thread enters into sleeping state
    Thread.sleep(1000);
    // Thread interrupting itself
    Thread.currentThread().interrupt();
    // check if the Thread is interrupted
    if (Thread.interrupted()) 
   {
       // Throwing CustomInterruptedException
       throw new CustomInterruptedException("Thread interrupted by throwing custom exception"); 
   } 
}

3.总结

这篇文章让我们了解了InterruptedException,它的构造函数,方法和产生的原因。我们也看到了在我们的代码中处理中断异常的各种方法。