java多线程之“线程中断”的理解

什么时候要用到中断:

  比如你开了生产者Producer和消费者Consumer两个线程,并用一个同步队列放置Porducer生产的和Consumer消费的产品。在Consumer中开启一个Producer线程,并且当Consumer不想消费时,可以随时结束掉Producer线程。如果不使用中断,可以使用一个boolean canceled标志,在Producer中循环检查该标志,如果该标志为true则退出。

1try{ 2 while(! cancelled){ 3 product = Producer.produce(); 4 blockingQueue.put(product); 5 } 6}catch(InterruptedException){}

  可是当blockingQueue被填满,Producer线程被堵塞在put()方法时,Consumer将cancelled设为true,并停止消费。在这种情况下,Producer线程将一直阻塞在put()方法处,永远检测不到cancel被置为true。

  BlockingQueue.put()方法是一个阻塞库方法,它可以及时检测到线程的中断状态。因此如果在Consumer中使用interrupt()方法中断Producer,那么即使Producer阻塞在put()处了,也能及时响应中断请求,结束生产线程。

线程中断到底是什么:

  “线程中断是一种协作机制,线程可以通过这种机制来通知另一个线程,告诉它在合适的或者可能的情况下停止当前工作。”

  每个线程都有一个boolean类型的中断变量,并且在Thread类中有三个方法,分别是

1public class Thread{ 2 public void interrupt(){...} 3 public boolean isInterrupted(){...} 4 public static boolean interrupted(){...} 5 ... 6}

  其他线程可以调用线程的interrupt()方法,通过将中断变量设为true来中断该线程;isInterrupted()方法返回线程的中断变量状态,但不会改变其状态值;而interrupted()方法会先清除中断状态(即将中断变量设为false),并返回之前的中断状态。

1System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted()); 2 Thread.currentThread().interrupt(); 3 System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted()); 4 System.out.println("thread is interrupted: " + Thread.interrupted()); 5 System.out.println("thread is interrupted: " + Thread.currentThread().isInterrupted());

  输出结果:

1thread is interrupted: false 2thread is interrupted: true 3thread is interrupted: true 4thread is interrupted: false

  可以看到,在使用interrupted()类方法后,线程的中断状态变为false。

怎么使用中断机制

  其实,中断操作(即调用线程的interrupte()方法)并不是会停止一个正在运行的线程,它只是把线程里边的中断变量设置为true,向这个线程发出一个中断请求,而线程怎么反应,则是线程自己的事情。它可以完全不理会中断,照常执行。举个栗子

1public class interruptTest { 2 private static class Endless extends Thread{ 3 public void run(){ 4 int i=0; 5 while (i<500){ 6 i++; 7 System.out.println(i); 8 } 9 } 10 } 11 public static void main(String[] args) throws InterruptedException{ 12 Thread endless = new Endless(); 13 endless.start(); 14 Thread.sleep(1); 15 endless.interrupt(); 16 System.out.println("main: I have interrupted endless"); 17 } 18}

  这段代码的输出为:

   虽然主线程试图中断endless,但它不为所动,还是把循环执行完了,正常退出。

  但不推荐线程进行如此任性的操作。当线程被发现被中断时,**要不然把异常抛出(停止运行,throws InterruptedException);要不然推迟中断请求(继续进行操作),但需要把中断信息传递给调用该线程的上层调用者。**对于第二种情况,来看一下下面的例子:

1class Consumer extends Thread{ 2 public void run(){ 3 boolean interrupt = false; 4 try { 5 while (true){ 6 try { 7 consume(blockingQueue.take()); 8 return; 9 }catch (InterruptedException e){ 10 interrupt = true; 11 } 12 } 13 }finally { 14 if (interrupt){ 15 Thread.currentThread().interrupt(); 16 } 17 18 } 19 } 20}  

  线程Consumer会执着地等到消费了一个从阻塞队列中拿到的产品后,才结束执行,即使该线程的中断状态被设为true,且被blockingQueue.take()方法发现,并抛出InterruptedException后。但它在finally中将线程的中断状态重新置为了true,以保证调用Consumer的上层线程能知道该线程被中断过。

  但现在出现了一个问题是,**为什么在finally中要调用interrupt()方法重新把线程中断状态置为true呢。**讲道理如果blockingQueue.take()方法发现了中断,那就证明线程中断状态已经是true了啊。原因就在于这个blockingQueue的take()方法。在阻塞库方法,如Thread.sleep(), Object.wait(),以及这里的BlockingQueue.take()方法中(其实从BlockingQueue.take()的源码中可以看到,在take()方法中调用了Object.wait()),都及时地响应当前线程的中断请求,并且在响应时执行两个操作:一是清除中断状态,二是抛出InterruptedException。清除中断状态就是把中断状态重新置为false,这就解释了为什么在finally中要重新调用interrupt()方法。

 什么是恢复中断

  现在可以来解释一下什么叫恢复中断,我理解的恢复中断,并不是把线程从BLOCKED或WAITING等状态中解救出来(本来中断也不是这么个意思),而是把线程的中断状态变量重新设置为True。

写在后面:

看《Java并发编程实战》时看到第7章开头就看不明白,发现自己其实一直不懂中断到底是个什么东西,一直傻傻地认为中断和线程阻塞是一个意思。。。然后查了一些博客,并且把不懂的地方反复看了好几遍,才稍稍有些明白了。写博客其实就是捋捋自己的思路,顺便记录一下,如有错误恳请指出。

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

java多线程之“线程中断”的理解 - HelloWorld