热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

中断线程_聊聊贯穿Java并发编程的中断机制!

|好看请赞,养成习惯你有一个思想,我有一个思想,我们交换后,一个人就有两个思想IfyoucanNOTexplainitsim

| 好看请赞,养成习惯

  • 你有一个思想,我有一个思想,我们交换后,一个人就有两个思想

  • If you can NOT explain it simply, you do NOT understand it well enough

横看成岭侧成峰,远近高低各不同,并发编程理论系列基本已经结束,相信大家有了理论的铺垫,近看源码才能发现其设计之美,不会一头雾水


本来是要介绍 AQS 作为我们走进并发编程源码环节的第一步,但 AQS 涉及的知识点也还真有点多,每一个都够单独拿出来说一说,恰巧有朋友私信我“不理解线程的中断机制”,中断机制又恰巧是 AQS API实现的一部分,更贯穿于整个并发编程内容中。于是就打算单独说一说这个小机制,先让大家做到心中有 number

在学习/编写并发程序时,总会听到/看到如下词汇:

  • 线程被中断或抛出InterruptedException
  • 设置了中断标识
  • 清空了中断标识
  • 判断线程是否被中断

在 Java Thread 类又提供了长相酷似,让人傻傻分不清的三个方法来处理并发中断问题:

  • interrupt()
  • interrupted()
  • isInterrupted()

aea87c5d52e4ac97437536891c32f1a5.png

看到这我不禁会问自己:

0ca5855730ed70069d31ba942cb6e3c9.png

什么是中断机制?

4591bd11508054aff4d213af9b52e183.png

刚刚接触【中断】这个词时,先入为主的概念就是“直接中断/打断”正在做的事,使其停止。我的理解是这样的:

你:在打游戏

女朋友:别打游戏了,赶快过来吃饭

你:听到女朋友招呼之后立马中断手中的游戏乖乖过去吃饭

38f87c1a7279b212c73c791923b36060.png

在多线程编程中,中断是一种【协同】机制,怎么理解这么高大上的词呢?就是女朋友叫你吃饭,你收到了中断游戏通知,但是否马上放下手中的游戏去吃饭看你心情 。在程序中怎样演绎这个心情就看具体的业务逻辑了,Java 的中断机制就是这么简单

如果还没改变这个先入为主的概念,我怀你没有女朋友(?)我们拥抱一下

为什么会有中断机制?

中断是一种协同机制,我觉得就是解决【当局者迷】的状况

现实中,你努力忘我没有昼夜的工作,如果再没有人告知你中断,你身体是吃不消的。

在多线程的场景中,有的线程可能迷失在怪圈无法自拔(自旋浪费资源),这时就可以用其他线程在恰当的时机给它个中断通知,被“中断”的线程可以选择在恰当的时机选择跳出怪圈,最大化的利用资源

那程序中如何中断?怎样识别是否中断?又如何处理中断呢?这就与上文提到的三个方法有关了

interrupt() VS isInterrupted() VS interrupted()

Java 的每个线程对象里都有一个 boolean 类型的标识,代表是否有中断请求,可你寻遍 Thread 类你也不会找到这个标识,因为这是通过底层 native 方法实现的。

interrupt()

interrupt() 方法是 唯一一个 可以将上面提到中断标志设置为 true 的方法,从这里可以看出,这是一个 Thread 类 public 的对象方法,所以可以推断出任何线程对象都可以调用该方法,进一步说明就是可以一个线程 interrupt 其他线程,也可以 interrupt 自己。其中,中断标识的设置是通过 native 方法 interrupt0 完成的

4089e259fc876d594a661baf5db8665a.png

在 Java 中,线程被中断的反应是不一样的,脾气不好的直接就抛出了 InterruptedException() ,

126d8823a59d69789a3cdb1de23e1efe.png

该方法注释上写的很清楚,当线程被阻塞在:

  1. wait()
  2. join()
  3. sleep()

这些方法时,如果被中断,就会抛出 InterruptedException 受检异常(也就是必须要求我们 catch 进行处理的)

熟悉 JUC 的朋友可能知道,其实被中断抛出 InterruptedException 的远远不止这几个方法,比如:

edcc2af9963855f8bfd0e626c600f791.png

反向推理,这些可能阻塞的方法如果声明有 throws InterruptedException , 也就暗示我们它们是可中断的

调用 interrput() 方法后,中断标识就被设置为 true 了,那我们怎么利用这个中断标识,来判断某个线程中断标识到底什么状态呢?

isInterrupted()

9a1829c56a11ad0e21bb1fe0649ea443.png

这个方法名起的非常好,因为比较符合我们 bean boolean 类型字段的 get 方法规范,没错,该方法就是返回中断标识的结果:

  • true:线程被中断,
  • false:线程没被中断或被清空了中断标识(如何清空我们一会儿看)

拿到这个标识后,线程就可以判断这个标识来执行后续的逻辑了。有起名好的,也有起名不好的,就是下面这个方法:

interrupted()

按照常规翻译,过去时时态,这就是“被打断了/被打断的”,其实和上面的 isInterrupted() 方法差不多,两个方法都是调用 private 的 isInterrupted() 方法, 唯一差别就是会清空中断标识(这是从方法名中怎么也看不出来的)

8dea11bed3cc2cdab12470ab8cc823e3.png

因为调用该方法,会返回当前中断标识,同时会清空中断标识,就有了那一段有点让人迷惑的方法注释:

c9f86749e208aff5efe58bd0f112a265.png

来段程序你就会明白上面注释的意思了:

Thread.currentThread().isInterrupted(); // true
Thread.interrupted() // true,返回true后清空了中断标识将其置为 false
Thread.currentThread().isInterrupted(); // false
Thread.interrupted() // false

这个方法总觉得很奇怪,现实中有什么用呢?

当你可能要被大量中断并且你想确保只处理一次中断时,就可以使用这个方法了

该方法在 JDK 源码中应用也非常多,比如(后续文章会具体分析,这里知道该方法的作用和使用场景就好):

841e28c27965fb796fba656ebb316f8a.png

相信到这里你已经能明确分辨三胞胎都是谁,并发挥怎样的作用了,那么有哪些场景我们可以使用中断机制呢?

中断机制的使用场景

通常,中断的使用场景有以下几个

  • 点击某个桌面应用中的关闭按钮时(比如你关闭 IDEA,不保存数据直接中断好吗?);
  • 某个操作超过了一定的执行时间限制需要中止时;
  • 多个线程做相同的事情,只要一个线程成功其它线程都可以取消时;
  • 一组线程中的一个或多个出现错误导致整组都无法继续时;

因为中断是一种协同机制,提供了更优雅中断方式,也提供了更多的灵活性,所以当遇到如上场景等,我们就可以考虑使用中断机制了

使用中断机制有哪些注意事项

其实使用中断机制无非就是注意上面说的两项内容:

  1. 中断标识
  2. InterruptedException

前浪已经将其总结为两个通用原则,我们后浪直接站在肩膀上用就可以了,来看一下这两个原则是什么:

原则-1

如果遇到的是可中断的阻塞方法, 并抛出 InterruptedException,可以继续向方法调用栈的上层抛出该异常;如果检测到中断,则可清除中断状态并抛出 InterruptedException,使当前方法也成为一个可中断的方法

原则-2

若有时候不太方便在方法上抛出 InterruptedException,比如要实现的某个接口中的方法签名上没有 throws InterruptedException,这时就可以捕获可中断方法的 InterruptedException 并通过 Thread.currentThread.interrupt() 来重新设置中断状态。

再通过个例子来加深一下理解:

本意是当前线程被中断之后,退出while(true),  你觉得代码有问题吗?(先不要向下看)

Thread th = Thread.currentThread();
while(true) {
  if(th.isInterrupted()) {
    break;
  }
  // 省略业务代码
  try {
    Thread.sleep(100);
  }catch (InterruptedException e){
    e.printStackTrace();
  }
}

打开 Thread.sleep 方法:

1da4c7efacef141548e030034b0f8686.png

sleep 方法抛出 InterruptedException后,中断标识也被清空置为 false,我们在catch 没有通过调用 th.interrupt() 方法再次将中断标识置为 true,这就导致无限循环了

这两个原则很好理解。总的来说,我们应该留意 InterruptedException,当我们捕获到该异常时,绝不可以默默的吞掉它,什么也不做,因为这会导致上层调用栈什么信息也获取不到。其实在编写程序时,捕获的任何受检异常我们都不应该吞掉

JDK 中有哪些使用中断机制的地方呢?

中断机制贯穿整个并发编程中,这里只简单列觉大家经常会使用的,我们可以通过阅读JDK源码来进一步了解中断机制以及学习如何使用中断机制

ThreadPoolExecutor

ThreadPoolExecutor 中的 shutdownNow 方法会遍历线程池中的工作线程并调用线程的 interrupt 方法来中断线程

610926473b8f180e6b23fd2a4ba6d97a.png

a5c802fd69c32c0138cf85df0679a5fe.png

FutureTask

FutureTask 中的 cancel 方法,如果传入的参数为 true,它将会在正在运行异步任务的线程上调用 interrupt 方法,如果正在执行的异步任务中的代码没有对中断做出响应,那么 cancel 方法中的参数将不会起到什么效果

3e2cdede59a3ce3a1d41e16f67334bf3.png

总结

到这里你应该理解Java 并发编程中断机制的含义了,它是一种协同机制,和你先入为主的概念完全不一样。区分了三个相近方法,说明了使用场景以及使用原则,同时又给出JDK源码一些常见案例,相信你已经胸中有沟壑了,接下来,跟上节奏,我们陆续走进源码吧

灵魂追问

  1. 抛出 InterruptedException 后,中断标识就一定被清空吗?
  2. 处在死锁状态的线程是否可以被中断呢?
  3. 进入临界区的线程能否被中断呢?如果不能有什么办法能响应中断吗?
  4. 个人感觉interrupted这个方法名称不是特别好,如果你也觉得不好,让你设计这个地方,你有什么想法?

参考

  1. Java 并发编程实战
  2. Java并发编程的艺术
  3. https://www.infoq.cn/article/java-interrupt-mechanism
  4. https://coderanch.com/t/237332/certification/explain-interrupt-isInterrupted-interrupted-method
  5. https://dzone.com/articles/waiting-for-coroutines

推荐阅读

  • Github标星34K+Star,这款开源项目助你秒建Git服务!
  • IntelliJ IDEA 更新后,电脑卡成球,该如何优化?
  • 我常用的自动化部署技巧,贼好用,推荐给大家!
  • 求求你了,不要再自己实现这些逻辑了,开源工具类不香吗?
  • 支持多线程的Redis 6.0终于发布了!
  • 我常用的IDEA插件大公开,个个是精品!
  • MongoDB快速入门,掌握这些刚刚好!
  • 我用起来顺手的数据库设计工具,这次推荐给大家!
  • 一个不容错过的Spring Cloud实战项目!
  • 我的Github开源项目,从0到20000 Star!

21ccd312d7d01fc8d69e222862e82d26.png

欢迎关注,点个在看




推荐阅读
  • Java太阳系小游戏分析和源码详解
    本文介绍了一个基于Java的太阳系小游戏的分析和源码详解。通过对面向对象的知识的学习和实践,作者实现了太阳系各行星绕太阳转的效果。文章详细介绍了游戏的设计思路和源码结构,包括工具类、常量、图片加载、面板等。通过这个小游戏的制作,读者可以巩固和应用所学的知识,如类的继承、方法的重载与重写、多态和封装等。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 开发笔记:加密&json&StringIO模块&BytesIO模块
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了加密&json&StringIO模块&BytesIO模块相关的知识,希望对你有一定的参考价值。一、加密加密 ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • 本文主要解析了Open judge C16H问题中涉及到的Magical Balls的快速幂和逆元算法,并给出了问题的解析和解决方法。详细介绍了问题的背景和规则,并给出了相应的算法解析和实现步骤。通过本文的解析,读者可以更好地理解和解决Open judge C16H问题中的Magical Balls部分。 ... [详细]
  • JavaSE笔试题-接口、抽象类、多态等问题解答
    本文解答了JavaSE笔试题中关于接口、抽象类、多态等问题。包括Math类的取整数方法、接口是否可继承、抽象类是否可实现接口、抽象类是否可继承具体类、抽象类中是否可以有静态main方法等问题。同时介绍了面向对象的特征,以及Java中实现多态的机制。 ... [详细]
  • 关于我们EMQ是一家全球领先的开源物联网基础设施软件供应商,服务新产业周期的IoT&5G、边缘计算与云计算市场,交付全球领先的开源物联网消息服务器和流处理数据 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • 上图是InnoDB存储引擎的结构。1、缓冲池InnoDB存储引擎是基于磁盘存储的,并将其中的记录按照页的方式进行管理。因此可以看作是基于磁盘的数据库系统。在数据库系统中,由于CPU速度 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
author-avatar
捕鱼达人2602929461
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有