java InterruptedException
背景
题主在写代码的是否,使用sleep()方法,方法抛出了这个异常,为了如何妥善处理这个异常,有了下面的总结。
在了解InterruptedException异常之前应该了解以下的几个关于线程的一些基础知识。而且得知道什么时候会抛InterruptedException异常(当阻塞方法收到中断请求的时候就会抛出InterruptedException异常)
1 什么是中断
线程中断(Thread Interruption)是 Java 中一种协调多线程应用中线程间通信和协作的机制。中断是一个线程通过【标记】来通知另一个线程,它应该停止当前在做的事情并尽快退出。线程中断机制提供了一种优雅和建议的方式来停止或通知线程。被通知的线程在适当的时候自我检查,决定是否终止、暂停或者进行其他处理。
工作原理和方法
线程通过设置一个内部的中断标志位来表示它是否已经被通知中断,默认为false,表示未被中断。当一个线程A调用了线程B的interrupt()方法时,那么线程B的是否请求的中断标志变为true,而线程B可以调用方法检测到此标志的变化。相关的方法有
t.interrupt() | 发出中断信号给线程t,设置t的中断标志为true。启动中断不会强行停止线程,而是通知线程应该停止执行,这种通知其实只是更改了线程的标志位而已,具体是否停止线程是由具体的逻辑决定的。 |
t.isInterrupted() | 检查线程t是否有中断状态,但不清除中断标志,就是不会修改重点标识。 |
Thread.interrupted() | 静态方法,返回并重置当前线程的中断状态,设置中断状态为true。用于线程操作检查和重置状态。 |
本质上,上面的方法就是在查看和更改标识位的值。
2 什么是中断异常 InterruptedException
【阻塞方法】收到中断请求的时候抛出的异常就是中断异常InterruptedException
为了理解阻塞方法要先说下面几个概念:
2.1线程的状态
线程在一定的条件下会发生状态的改变,下面是线程的一些状态
- 初始(NEW):新建一个线程的对象,还未调用start方法
- 运行(RUNNABLE):java线程中将已经准备就绪(Ready)和正在运行中(Running)的两种状态都统称为“Runnable”。准备就绪的线程会被放在线程池中等待被调用
- 阻塞(BLOCKED):是因为各种原因进入了无法获取锁,无法获取CPU的使用权,暂时的停止了运行。直到线程获取锁,才会进入准备就绪(Ready)状态才会有机会转到运行状态
- 等待(WAITING):该状态的线程需要等待其他线程做出一些特定的动作(通知或者是中断)
- 超时等待(TIME_WAITING):该状态和上面的等待不同,他可以在指定的时间内自行返回
- 终止(TERMINATED):线程任务执行完毕
2.2 线程阻塞
线程阻塞通常是指一个线程在执行过程中暂停,以等待某个条件的触发。而什么情况才会使得线程进入阻塞的状态呢?
- 等待阻塞:运行的线程执行wait()方法,该线程会释放占用的所有资源,JVM会把该线程放入“等待池”中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程调用notify()或notifyAll()方法才能被唤醒
- 同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则JVM会把该线程放入“锁池”中
- 其他阻塞:运行的线程执行sleep()或join()方法,或者发出了I/O请求时,JVM会把该线程置为阻塞状态。当sleep()状态超时、join()等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态
2.3阻塞方法
阻塞方法:就是会抛出InterruptedException异常的方法。
如果线程B调用了阻塞方法+中断标志变为了true,那么它会抛出InterruptedException异常。抛出异常的同时它会将线程B的是否请求中断标志置为false。
抛InterruptedException的代表方法有:sleep(),wait(),join()。
这三个方法在执行过程中会不断轮询中断状态,如果中断=true,从而自己抛出InterruptEdException。
2.4代码示例
package com.autonavi.cloudmap.xd.biz.impl;
/**
* @program: Test
* @description:
**/
public class InterrupTest implements Runnable{
public void run(){
try {
while (true) {
Boolean a = Thread.currentThread().isInterrupted();
System.out.println("in run() - about to sleep for 20 seconds -" + a);
Thread.sleep(20000);
System.out.println("in run() - woke up");
}
} catch (InterruptedException e) {
System.out.println("1"+Thread.currentThread().isInterrupted());
Thread.currentThread().interrupt();
System.out.println("2"+Thread.currentThread().isInterrupted());
Boolean c=Thread.interrupted();
System.out.println("c="+c);
System.out.println("3"+Thread.currentThread().isInterrupted());
Boolean d=Thread.interrupted();
System.out.println("d="+d);
System.out.println("4"+Thread.currentThread().isInterrupted());
}
}
public static void main(String[] args) {
InterrupTest si = new InterrupTest();
Thread t = new Thread(si);
t.start();
//主线程休眠2秒,从而确保刚才启动的线程有机会执行一段时间
try {
Thread.sleep(2000);
}catch(InterruptedException e){
e.printStackTrace();
}
System.out.println("in main() - interrupting other thread");
//中断线程t
t.interrupt();
System.out.println("in main() - leaving");
}
}
返回结果:
in run() - about to sleep for 20 seconds-------false
in main() - interrupting other thread
in main() - leaving
1false
2true
c=true
3false
d=false
4false
Process finished with exit code 0
3 对InterruptedException异常的处理
在实际处理中,sleep(),wait(),join()本身会抛出InterruptedException,对这个异常的处理本质是对中断状态的处理。
1、没有基于中断信息的逻辑处理
如果是在单线程或多线程没有对中断进行通讯和控制的相关内容,打日志或者对线程中断状态进行具体的控制其实没什么区别,因为并不关心这个中断状态的信息有什么用。这里推荐的方式是,catch后,恢复中断状态为true,以防未来这个中断信息有需要的时候。
2、有基于中断信息的逻辑处理
在具体需要根据线程的中断状态进行控制的情况下,就可以按照逻辑需要,通过Thread.interrupted();将中断状态改为自己需要的值。在后续具体的逻辑里进行程序的处理和终止等操作。
不推荐catch后向上抛出,虽然可以转换为RuntimeException抛出,但是有几个不好的点:首先这样中断了线程的执行。如果目标就是中断线程,应该利用中断信息位做更优雅的操作;其次,抛出RuntimeException也屏蔽了具体的中断细节,在多层调用结构里,会造成难以定位问题的问题。
4、代码示例
1. 检查中断标志位时结束运行
线程可以在适当的地方检查自己的中断状态,并在检测到中断时自行退出。常用的方法是 Thread.currentThread().isInterrupted()
。
public class InterruptExample {
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("Thread is running...");
// 执行一些任务
try {
Thread.sleep(1000); // sleep 会响应中断并抛出异常
} catch (InterruptedException e) {
System.out.println("Thread is interrupted during sleep.");
Thread.currentThread().interrupt(); // 恢复中断状态以便进一步检查
}
}
System.out.println("Thread is exiting...");
});
thread.start();
Thread.sleep(3000); // 让线程运行一段时间
thread.interrupt(); // 发送中断信号
}
}
明确的任务中断检查和资源清理
复杂任务中必须在多个地方进行中断检查,并进行必要的资源清理。
public class ComplexTaskExample {
public static void main(String[] args) {
Thread thread = new Thread(new ComplexTask());
thread.start();
try {
Thread.sleep(3000); // 主线程休眠3秒
thread.interrupt(); // 中断任务线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class ComplexTask implements Runnable {
@Override
public void run() {
try {
performTask1();
performTask2();
performTask3();
} catch (InterruptedException e) {
System.out.println("Task was interrupted.");
// 执行必要的清理资源
cleanUp();
}
System.out.println("Task exiting...");
}
private void performTask1() throws InterruptedException {
System.out.println("Performing Task 1");
// 处理过程,如果中断
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
private void performTask2() throws InterruptedException {
System.out.println("Performing Task 2");
// 模拟阻塞
Thread.sleep(2000);
}
private void performTask3() throws InterruptedException {
System.out.println("Performing Task 3");
// 处理过程,如果中断
if (Thread.currentThread().isInterrupted()) {
throw new InterruptedException();
}
}
private void cleanUp() {
System.out.println("Cleaning up resources...");
}
}
参考链接:
Java线程状态分析/线程状态转换图_jdk 线程状态图-CSDN博客必须要掌握的 InterruptedException 异常处理-腾讯云开发者社区-腾讯云
InterruptedException异常处理方式-CSDN博客