大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
前言
Java中的线程,有一个状态叫做 中断状态
,用于标记线程是否被中断过,通过对线程中断状态的判断,可以实现例如 优雅终止线程
和 唤醒线程
等功能。
在Thread类中有interrupt(),interrupted()和isInterrupted() 方法与线程的中断有关,本篇文章将对这些方法的具体作用进行详细解释。
在文章的最后,还会通过一个典型例子,演示如何通过线程的中断状态来优雅的终止线程。
正文
一. interrupt()方法详解
Thread类提供了interrupt() 方法来中断线程,哪个线程对象的interrupt() 方法被调用,那么这个线程就会被中断。关于interrupt() 方法,有如下注意点。
- interrupt() 方法是成员方法;
- 通常不能在线程内部调用线程的 interrupt() 方法来中断线程自己;
- 如果线程正阻塞在 Object#wait,Object.wait(long),Object.wait(long, int),Thread#join,Thread.join(long),Thread.join(long, int),Thread.sleep(long) 或 Thread.sleep(long, int) 方法上,然后被 interrupt() 方法中断,此时中断状态会被重置为 false(false 表示未被中断),并且被中断的线程会收到中断异常 InterruptedException。
上面最重要的就是第3点,第3点中罗列出来的方法在使用时都需要try-catch中断异常InterruptedException,但是在这个中断异常被抛出前,线程的中断状态会被重置为false,这就造成一种现象,因为被中断而从阻塞方法中唤醒的线程的中断状态是false。
二. interrupted()方法详解
Thread类提供了静态方法interrupted() 来得到调用该方法的线程的中断状态。关于interrupted() 方法,有如下注意点。
- interrupted() 方法是静态方法;
- 哪个线程调用 interrupted() 方法,就会返回这个线程的中断状态,然后线程的中断状态会重置为 false。
interrupted() 方法在调用后,调用线程的中断状态会重置为false,这一点请切记。
三. isInterrupted()方法详解
Thread类提供了isInterrupted() 方法来得到线程的中断状态,哪个线程对象的isInterrupted() 方法被调用,就会返回这个线程的中断状态。关于isInterrupted() 方法,有如下注意点。
- isInterrupted() 方法是成员方法;
- 哪个线程对象的 isInterrupted() 方法被调用,就会返回这个线程的中断状态,并且中断状态不会重置为 false。
isInterrupted() 方法和interrupted() 方法都能获取到线程的中断状态,不同点在于isInterrupted() 方法获取到线程的中断状态后,线程的中断状态不会重置为false,而interrupted() 方法会。
四. 优雅的终止线程
首先明确一点,一个线程的终止,不能由其它线程来强行终止
,这也就是为什么Thread#stop,Thread#suspend等方法会被废弃。
优雅终止线程的简单思路如下。
在线程执行的任务中不断的判断一个标志位,当标志位满足某种条件时,任务结束运行,从而线程优雅终止。
如下是一个简单示例。
public class InterruptedTest {
@Test
public void 优雅终止线程的简单示例() {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 执行任务
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
Thread thread = Thread.currentThread();
while (!thread.isInterrupted()) {
// empty run
}
System.out.println(thread.getName() + " 任务执行完毕");
});
}
// 主线程等待2秒,等待任务充分执行
LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);
// shutdownNow线程池
threadPool.shutdownNow();
// 主线程等待1秒,等待任务关闭
LockSupport.parkNanos(1000 * 1000 * 1000);
}
}
上述示例中,每个线程执行的任务中会一直判断当前线程的中断状态,如果中断状态为true(表示线程被中断了),那么就退出while循环,从而任务执行完毕,最终线程被优雅终止。
运行测试程序,打印如下。
pool-1-thread-5 任务执行完毕
pool-1-thread-4 任务执行完毕
pool-1-thread-3 任务执行完毕
pool-1-thread-2 任务执行完毕
pool-1-thread-1 任务执行完毕
运行结果表明线程确实被终止了。上述示例中线程执行的任务实际上是 响应中断
的,因此在线程被调用interrupt() 方法时,能够响应中断,从而结束任务的执行,最终线程终止。
再看如下一个例子,是一个看起来响应中断,却实际不响应中断的典型错误案例。
public class InterruptedTest {
@Test
public void 线程池中的任务不响应中断的典型案例() {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 执行任务
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
Thread thread = Thread.currentThread();
while (!thread.isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
System.out.println(thread.getName() + " 被中断了");
}
}
System.out.println(thread.getName() + " 任务执行完毕");
});
}
// 主线程等待2秒,等待任务充分执行
LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);
// shutdownNow线程池
threadPool.shutdownNow();
// 主线程等待1秒,等待任务关闭
LockSupport.parkNanos(1000 * 1000 * 1000);
}
}
上述示例中,任务大部分时间是阻塞在Thread.sleep(long) 方法上的,但其实Thread.sleep(long) 方法是响应中断的,所以上述示例是希望线程被中断时,线程从Thread.sleep(long) 方法返回,然后判断线程的中断状态,然后结束任务的运行,最终线程终止。
运行测试程序,打印结果如下。
pool-1-thread-1 被中断了
pool-1-thread-3 被中断了
pool-1-thread-4 被中断了
pool-1-thread-2 被中断了
pool-1-thread-5 被中断了
可见任务没有结束运行,故线程也并没有被终止。原因就是第一节中提到的,线程阻塞在Thread.sleep(long) 方法上时如果被中断,那么中断状态会被重置为false然后抛出中断异常InterruptedException,那么上述示例中,任务在判断线程的中断状态时,中断状态会恒为false,故任务永远不会退出。
改进代码如下所示。
public class InterruptedTest {
@Test
public void 线程池中的任务不响应中断的典型案例改进() {
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 5,
60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
// 执行任务
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
Thread thread = Thread.currentThread();
while (!thread.isInterrupted()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// 重新将中断状态置为true
thread.interrupt();
System.out.println(thread.getName() + " 被中断了");
}
}
System.out.println(thread.getName() + " 任务执行完毕");
});
}
// 主线程等待2秒,等待任务充分执行
LockSupport.parkNanos(1000 * 1000 * 1000 * 2L);
// shutdownNow线程池
threadPool.shutdownNow();
// 主线程等待1秒,等待任务关闭
LockSupport.parkNanos(1000 * 1000 * 1000);
}
}
改进思路就是在任务中捕获到InterruptedException后,再调用一次当前线程对象的interrupt() 方法,来将当前线程的中断状态重新置为true,从而任务可以顺利结束运行,最终线程终止。
运行测试程序,打印结果如下。
pool-1-thread-5 被中断了
pool-1-thread-5 任务执行完毕
pool-1-thread-1 被中断了
pool-1-thread-1 任务执行完毕
pool-1-thread-2 被中断了
pool-1-thread-2 任务执行完毕
pool-1-thread-3 被中断了
pool-1-thread-4 被中断了
pool-1-thread-4 任务执行完毕
pool-1-thread-3 任务执行完毕
可见任务是顺利执行完毕,从而线程完成了优雅终止。
总结
关于Java中线程的 中断
总结如下。
- Thread 类提供了 interrupt() 成员方法来中断线程。哪个线程对象的interrupt() 方法被调用,那么这个线程被中断,中断状态会被置为true。但是如果线程是阻塞在Object#wait,Thread#sleep等方法上时被调用interrupt() 方法来中断,那么中断状态会被重置为false,然后抛出中断异常InterruptedException;
- Thread 类提供了 interrupted() 静态方法来获取线程的中断状态。哪个线程调用interrupted() 方法,就返回这个线程的中断状态,同时也
会
重置这个线程的中断状态为false; - Thread 类提供了 isInterrupted() 成员方法来获取线程的中断状态。哪个线程对象的isInterrupted() 方法被调用,那么就返回这个线程的中断状态,并且
不会
重置这个线程的中断状态为false;
线程的 优雅终止
思路如下。
在线程执行的任务中不断的判断一个标志位,当标志位满足某种条件时,任务结束运行,从而线程优雅终止。
大家好,我是半夏之沫 😁😁 一名金融科技领域的JAVA系统研发😊😊
我希望将自己工作和学习中的经验以最朴实,最严谨的方式分享给大家,共同进步👉💓👈
👉👉👉👉👉👉👉👉💓写作不易,期待大家的关注和点赞💓👈👈👈👈👈👈👈👈
👉👉👉👉👉👉👉👉💓关注微信公众号【技术探界】 💓👈👈👈👈👈👈👈👈
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。