java kill线程_如何优雅的 kill 线程

84009465f98cdc09743d6f75c2f89d31.png

kill 掉一个线程,感觉是一件很简单的事情,比如 JAVA 中为我们提供了 stop 方法可以立即终止线程的执行,达到 kill 掉线程的目的。

但实际上对线程的操作是一件精细活,对于一段正在执行的任务,我们不能只是简单粗暴的勒令其停止。原因就是,线程与资源是有关联的。

比如,一个线程持有某个 lock ,我们在线程释放 lock 前粗暴的停止了它的运行,那么可能导致其持有的 lock 永远不能被释放,等待在该 lock 上的其它线程也会永远的阻塞在该处永远无法继续执行。

再比如,一个线程正在进行 IO 操作,占用着硬盘中的某个文件,我们粗暴的终止了该线程,则这个文件便得不到释放,会一直处于被占用状态。

进程是资源分配单位,线程是运行调度单位。但这并不意味着,线程可以不考虑资源的调度随意的执行任务。恰恰相反的是,许多由线程发起申请得到的资源,只有发起申请的线程才能正确的释放它们。因为即使其对同一进程中的其它线程可见,其它线程也并不知道释放该资源的正确时机。

所以我们在终止线程时,不能采用类似 stop 这种简单粗暴的方式。应该确保在终止线程时,线程并不是立即被终止,而是将控制权交给该线程,由该线程自行判断应该做好哪些善后工作,做完这些善后工作后再停止。

JAVA 中的 interrupt 信号可以做到这一点,一个典型的安全终止线程的方式便是基于 interrupt 信号通知线程,以及通过 InterruptedException 异常回滚堆栈钩出阻塞中的线程,使其停止。

关于 JVM 是如何响应 interrupt 信号并抛出 InterruptedException 的,前面的博客有非常全面的解释:https://www.cnblogs.com/niuyourou/p/12392942.html。

我们来看一个 interrupt 终止线程的例子,一个典型的两步终止:

public class ThreadInterrupt extendsThread{

@Overridepublic voidrun(){int i=0;try{while (!Thread.currentThread().isInterrupted()) {

System.out.println("线程进行第 " + i + " 次工作,此时 inerrupt 标志位为:"+Thread.currentThread().isInterrupted());

Thread.sleep(100);

i++;

}//这里可以进行善后工作,比如释放资源

System.out.println("线程接收到 interrupt 信号,离开工作区.此时 inerrupt 标志位为:"+Thread.currentThread().isInterrupted());

}catch(InterruptedException inE){//这里可以进行善后工作,比如释放资源

System.out.println("线程在 sleep 的过程中被 interrupted,此时 interrupt 标志位为:"+Thread.currentThread().isInterrupted());

}

}

}

线程每执行完一次任务,都会检查一下是否有 interrupt 信号,如果有则进行善后工作并退出线程。

同时线程对 InterruptedException 进行了捕获,如果在 sleep/wait/join 过程中收到 interrupt 信号,则回滚堆栈到 try 处,进入 catch 块进行异常的处理,我们可以在catch 块中进行善后工作。

这样无论是运行状态还是阻塞状态(因为 sleep/wait/join 陷入的阻塞,因为我们只有通过这些方法可以使线程陷入阻塞。synchronized 等方法阻塞或唤醒线程是由 JVM 控制的,由 JVM 保证其正确性。)下的线程,都可以及时的响应我们的 interrupt 信号。

我们进行一下测试:

public classTest {public static void main(String[] args) throwsInterruptedException{

Thread test1=newThreadInterrupt();

test1.start();

Thread.sleep(1000);

test1.interrupt();

}

}

看一下效果,在 sleep 过程中接收到了 interrupt 信号:

948bb2021b8c4b9cfa8d3002007676ad.png

在正常工作时接收到了 interrupt 信号:

97b9d2bde311cad7fd9e6be8e1211fcd.png

我们可以看到,两次被 interrupt 线程表现并不同。

sleep 情况下进入 catch 块后,interrupt 状态被重置为了 false,而正常状态下被中断时 interrupt 状态未被重置,依然是true。

因为 interrupt 的本质是一个信号,正常情况下我们对一个信号进行了处理应该将其移出信号队列(当然这里不是队列),避免对其重复处理。

sleep 抛出异常前 JVM 帮助我们做到了这一点,我们在对信号进行处理时,也应该手动将其重置。

isInterrupted 只能获取信号状态,不会重置它。interrupted 可以返回信号的当前状态,如果未 true,返回后会将其重置为 false 。我们可以使用 interrupted 方法代替 isInterrupted 方法:

public class ThreadInterrupt extendsThread{

@Overridepublic voidrun(){int i=0;try{while (!Thread.interrupted()) {

System.out.println("线程进行第 " + i + " 次工作,此时 inerrupt 标志位为:"+Thread.currentThread().isInterrupted());

Thread.sleep(1);

i++;

}//这里可以进行善后工作,比如释放资源

System.out.println("线程接收到 interrupt 信号,离开工作区.此时 inerrupt 标志位为:"+Thread.currentThread().isInterrupted());

}catch(InterruptedException inE){//这里可以进行善后工作,比如释放资源

System.out.println("线程在 sleep 的过程中被 interrupted,此时 interrupt 标志位为:"+Thread.currentThread().isInterrupted());

}

}

}

这样再来看效果:

c3898499531b68c18298dc9ebfb006b4.png

可以看到,在响应了中断信号后,interrupted 方法帮我们将 interrupt 状态重置为了 false。

第 50 次工作时,检测到了 interrupt 信号,但线程还是将其执行完毕后才退出了工作区,这也证实了该方法的可靠性,使线程尽量安全的退出。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值