文章目录
-
- 引言
- java中的中断
-
- 何时触发中断阻塞
- 如何响应中断
- 中断的一些实践
-
- 基于标识取消任务
- 如何处理阻塞式的中断
- 合理的中断策略
- 时刻保留中断的状态
- 超时任务取消的最优解
- 处理系统层面阻塞IO
- 小结
- 参考
引言
我们通过并发编程提升了系统的吞吐量,特定场景下我们希望并发的线程能够及时的合理的停止,所以就来聊聊java并发编程中中断的哲学。
java中的中断
何时触发中断阻塞
当出现以下几种情况,线程就会触发阻塞或者暂停:
- 等待IO操作返回
- 等待线程休眠醒来
- 等待获取java同步锁
此时,线程就会处于BLOCKED
、WAITING
、TIMED_WAITING
、几种状态
如何响应中断
在操作系统中,线程的中断方式一般有两种:
- 抢占式:当线程需要中断时,直接让线程立刻停止手里的任务。
- 协作式:当线程需要中断时,通过标识告知线程需要被中断,线程轮询时看到这个标识就会直接中断。
而Java线程则是采用协作式中断,通知对于中断的后线程触发InterruptedException
,通常来说我们对线程中断响应度越高,就越容易及时取消一些非必要的操作,对应按照如下两种方式进行响应:
如果当前业务层面不具备处理此类异常的能力,那么就将异常向上层抛出传递给调用者:
public void func() throws InterruptedException {
Thread.interrupted();
}
如果类似于Runnable
这种无法向上抛出的内置接口类的实现,我们则可以主动去捕获中断,并将当前线程打断从而让高层栈帧感知到这个异常中断:
class Task implements Runnable {
@Override
public void run() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//打断当前线程引发更高层线程响应此中断
Thread.currentThread().interrupt();
}
}
}
中断的一些实践
基于标识取消任务
所以对于线程的的中断,我们可以采用协作式标识来停止线程,通过任务运行时轮询检测,一旦看到标识设置为true即已请求取消,则提前结束运行。
就像下面这段代码,我希望的任务是每个1s输出一次,为了让任务的能够及时停止,我们采用volatile标识cancelled,这样就能取消操作尽可能及时可见:
public class Task implements Runnable {
/**
* 使用volatile修饰保证标识修改可见
*/
private volatile boolean cancelled = false;
private final Thread thread = new Thread(this);
public void start() {
thread.start();
}
/**
* 停止时,通过cancel请求取消
*/
public void cancel() {
cancelled = true;
}
@Override
public void run() {
//取消标识检测,如果取消则直接结束循环
while (!cancelled) {
System.out.println("running");
ThreadUtil.sleep(1000)