Java并发 线程的中断

Tips

  • synchronized块是不能被中断的:在synchronized关键字阻塞等待获取锁时不可中断,但进入同步块后可被中断,表现同下文对两种线程状态的中断。参考: 为什么Synchronized不可中断?-腾讯云开发者社区-腾讯云
  • ReentrantLock支持可中断的获取模式,即 ReentrantLock.lockInterruptibly()。
  • 不是所有的阻塞方法收到中断后都可以取消阻塞状态,输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态。

1,中断两种状态的线程


强行终止是很危险的事情,优雅的方法就是,给那个线程一个中断信号, 让它自己决定该怎么办,这就是中断。
中断是通过调用Thread.interrupt()方法来执行的:
  1. 中断非阻塞中的线程:只是改变了中断标记,即Thread.isInterrupted()将返回true。
  2. 中断可取消的阻塞状态中的线程:比如sleep、wait、join,这个线程收到中断信号后,会抛出InterruptedException,同时会把中断状态置回为false。 如果先中断然后才sleep,也是会执行到sleep的时候立马抛异常(中断标志为true了)。
注意:如果线程是阻塞在LockSupport . park ()上,给线程中断后,不会抛出异常,线程唤醒继续执行,且线程中断标志为true。

2,终止“阻塞状态”的线程


通常,我们通过“中断”方式终止处于“阻塞状态”的线程。
当线程由于被调用了sleep()、wait()、join()等方法而进入阻塞状态,若此时调用线程的interrupt()将线程的中断标记设为true。
由于处于阻塞状态,中断标记会被重置到fasle ,同时产生一个InterruptedException异常。将InterruptedException放在适当的为止就能终止线程,形式如下:
@Override
public void run() {
    try {
        while (true) {
            // 执行任务…
            Thread.sleep(1000);
        }
    } catch (InterruptedException ie) { 
        // 由于产生InterruptedException异常,退出while(true)循环,线程终止!
    }
}
说明:在while(true)中不断的执行任务,当线程处于阻塞状态时,调用线程的interrupt()产生InterruptedException中断。中断的捕获在while(true)之外,这样就退出了while(true)循环!
注意:对InterruptedException的捕获务一般放在while(true)循环体的外面,否则就需要额外处理退出循环。形式如下:
@Override
public void run() {
    while (true) {
        try {
            // 执行任务...
        } catch (InterruptedException ie) { 
            // InterruptedException在while(true)循环体内。
            // 当线程产生了InterruptedException异常时,while(true)仍能继续运行!需要手动退出
            break;
        }
    }
}

3,终止“运行状态”的线程


通常,我们通过“标记”方式终止处于“运行状态”的线程。其中,包括“中断标记”和“额外添加标记”。
(01) 通过“中断标记”终止线程。形式如下:
@Override
public void run() {
    while (!isInterrupted()) {
        // 执行任务...
    }
}

说明:isInterrupted()是判断线程的中断标记是不是为true。

注意:interrupt()并不会终止处于“运行状态”的线程!它会将线程的中断标记设为true。

(02) 通过“额外添加标记”。形式如下:
private volatile boolean flag = true;
protected void stopTask() {
    flag = false;
}

@Override
public void run() {
    while (flag) {
        // 执行任务...
    }
}

注意:将flag定义为volatile类型,是为了保证flag的可见性。

4,通用的终止线程形式


综合线程处于“阻塞状态”和“运行状态”的终止方式,比较通用的终止线程的形式如下:
@Override
public void run() {
    try {
        // 1. isInterrupted()保证,只要中断标记为true就终止线程。
        while (!isInterrupted()) {
            // 执行任务...
        }
    } catch (InterruptedException e) { 
        // 2. InterruptedException异常保证,当InterruptedException异常产生时,线程被终止。
    }
}
 

5,实际应用


由于 处于阻塞状态的线程,被中断后抛出exception并置回中断状态,有时候是不利的,因为这个中断状态可能会作为别的线程的判断条件,所以稳妥的办法是在处理exception的地方把状态复位:
boolean interrupted = false;
try {
    while (true) {
        try {
            return blockingQueue.take();
        } catch (InterruptedException e) {
            interrupted = true;    
            break;
        }
    }
} finally {
    if (interrupted){
        Thread.currentThread().interrupt();
    }
}
当代码调用中须要抛出一个InterruptedException,你可以选择把中断状态复位,也可以选择向外抛出InterruptedException,由外层的调用者来决定。
  • 7
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值