当前位置: 首页 > news >正文

Java——线程的中断

线程的中断1、取消/关闭的场景2、取消/关闭的机制3、线程对中断的反应3.1、Runnable3.2、Waiting/Timed_Waiting3.3、Blocked3.4、New/Terminate4、如何正确地取消/关闭线程1、取消/关闭的场景我们知道通过线程的start方法启动一个线程后线程开始执行run方法run方法运行结束后线程退出那为什么还需要结束一个线程呢有多种情况比如很多线程的运行模式是死循环比如在生产者/消费者模式中消费者主体就是一个死循环它不停地从队列中接受任务执行任务在停止程序时我们需要一种“优雅”的方法以关闭该线程。在一些图形用户界面程序中线程是用户启动的完成一些任务比如从远程服务器上下载一个文件在下载过程中用户可能会希望取消该任务。在一些场景中比如从第三方服务器查询一个结果我们希望在限定的时间内得到结果如果得不到我们会希望取消该任务。有时我们会启动多个线程做同一件事比如类似抢火车票我们可能会让多个好友帮忙从多个渠道买火车票只要有一个渠道买到了我们会通知取消其他渠道。2、取消/关闭的机制Java的Thread类定义了如下方法publicfinalvoidstop()这个方法看上去就可以停止线程但这个方法被标记为了过时简单地说我们不应该使用它可以忽略它。在Java中停止一个线程的主要机制是中断中断并不是强迫终止一个线程它是一种协作机制是给线程传递一个取消信号但是由线程来决定如何以及何时退出。Thread类定义了如下关于中断的方法publicbooleanisInterrupted()publicvoidinterrupt()publicstaticbooleaninterrupted()这三个方法名字类似比较容易混淆我们解释一下。isInterrupted()和interrupt()是实例方法调用它们需要通过线程对象interrupted()是静态方法实际会调用Thread. currentThread()操作当前线程。每个线程都有一个标志位表示该线程是否被中断了。isInterrupted返回对应线程的中断标志位是否为true。interrupted返回当前线程的中断标志位是否为true但它还有一个重要的副作用就是清空中断标志位也就是说连续两次调用interrupted()第一次返回的结果为true第二次一般就是false除非同时又发生了一次中断​。interrupt表示中断对应的线程。中断具体意味着什么呢下面我们进一步来说明。3、线程对中断的反应interrupt()对线程的影响与线程的状态和在进行的IO操作有关。我们主要考虑线程的状态IO操作的影响和具体IO以及操作系统有关我们就不讨论了。线程状态有RUNNABLE线程在运行或具备运行条件只是在等待操作系统调度。WAITING/TIMED_WAITING线程在等待某个条件或超时。BLOCKED线程在等待锁试图进入同步块。NEW/TERMINATED线程还未启动或已结束。3.1、Runnable如果线程在运行中且没有执行IO操作interrupt()只是会设置线程的中断标志位没有任何其他作用。线程应该在运行过程中合适的位置检查中断标志位比如如果主体代码是一个循环可以在循环开始处进行检查如下所示publicclassInterruptRunnableDemoextendsThread{Overridepublicvoidrun(){while(!Thread.currentThread().isInterrupted()){//…单次循环代码}System.out.println(done );}//其他代码}3.2、Waiting/Timed_Waiting线程调用join/wait/sleep方法会进入WAITING或TIMED_WAITING状态在这些状态时对线程对象调用interrupt()会使得该线程抛出InterruptedException。需要注意的是抛出异常后中断标志位会被清空而不是被设置。比如执行如下代码ThreadtnewThread(){Overridepublicvoidrun(){try{Thread.sleep(1000);}catch(InterruptedExceptione){System.out.println(isInterrupted());}}};t.start();try{Thread.sleep(100);}catch(InterruptedExceptione){}t.interrupt();程序的输出为false。InterruptedException是一个受检异常线程必须进行处理。我们在异常处理中介绍过处理异常的基本思路是如果知道怎么处理就进行处理如果不知道就应该向上传递通常情况下不应该捕获异常然后忽略。捕获到InterruptedException通常表示希望结束该线程线程大致有两种处理方式向上传递该异常这使得该方法也变成了一个可中断的方法需要调用者进行处理有些情况不能向上传递异常比如Thread的run方法它的声明是固定的不能抛出任何受检异常这时应该捕获异常进行合适的清理操作清理后一般应该调用Thread的interrupt方法设置中断标志位使得其他代码有办法知道它发生了中断。第一种方式的示例代码如下publicvoidinterruptibleMethod()throwsInterruptedException{//…包含wait, join 或 sleep 方法Thread.sleep(1000);}第二种方式的示例代码如下publicclassInterruptWaitingDemoextendsThread{Overridepublicvoidrun(){while(!Thread.currentThread().isInterrupted()){try{//模拟任务代码Thread.sleep(2000);}catch(InterruptedExceptione){//...清理操作//重设中断标志位Thread.currentThread().interrupt();}}System.out.println(isInterrupted());}publicstaticvoidmain(String[]args){InterruptWaitingDemodemonewInterruptWaitingDemo();demo.start();demo.interrupt();}}3.3、Blocked如果线程在等待锁对线程对象调用interrupt()只是会设置线程的中断标志位线程依然会处于BLOCKED状态也就是说interrupt()并不能使一个在等待锁的线程真正“中断”​。我们看段代码publicclassInterruptSynchronizedDemo{privatestaticObjectlocknewObject();privatestaticclassAextendsThread{Overridepublicvoidrun(){synchronized(lock){while(!Thread.currentThread().isInterrupted()){}}System.out.println(exit);}}publicstaticvoidtest()throwsInterruptedException{synchronized(lock){AanewA();a.start();Thread.sleep(1000);a.interrupt();a.join();}}publicstaticvoidmain(String[]args)throwsInterruptedException{test();}}test方法在持有锁lock的情况下启动线程a而线程a也去尝试获得锁lock所以会进入锁等待队列随后test调用线程a的interrupt方法并调用join等待线程线程a结束线程a会结束吗不会interrupt方法只会设置线程的中断标志而并不会使它从锁等待队列中出来。在使用synchronized关键字获取锁的过程中不响应中断请求这是synchronized的局限性。如果这对程序是一个问题应该使用显式锁。3.4、New/Terminate如果线程尚未启动NEW​或者已经结束TERMINATED​则调用interrupt()对它没有任何效果中断标志位也不会被设置。4、如何正确地取消/关闭线程interrupt方法不一定会真正“中断”线程它只是一种协作机制如果不明白线程在做什么不应该贸然地调用线程的interrupt方法以为这样就能取消线程。对于以线程提供服务的程序模块而言它应该封装取消/关闭操作提供单独的取消/关闭方法给调用者外部调用者应该调用这些方法而不是直接调用interrupt。Java并发库的一些代码就提供了单独的取消/关闭方法比如Future接口提供了如下方法以取消任务booleancancel(booleanmayInterruptIfRunning);再如ExecutorService提供了如下两个关闭方法voidshutdown();ListRunnableshutdownNow();
http://www.gsyq.cn/news/1294268.html

相关文章:

  • Input Leap:一款让多设备共享键盘鼠标变得简单高效的开源KVM软件
  • RK3576开发板AIoT实战:从模型转换到边缘部署全流程解析
  • 【效率革命】3DMAX破损艺术:PolyDamage插件核心参数深度解析与实战调优
  • 深度学习篇---解空间
  • Verilog仿真‘随机数’不随机?深度解析$random的种子(seed)机制与可控复现
  • 一站式文档下载解决方案:kill-doc完全指南与实用技巧
  • Linux线程通信实战:POSIX消息队列原理与应用详解
  • Linux系统版本信息全面解析:从内核到发行版的运维必备技能
  • MacBook上从零配置Go环境:用Homebrew安装Go 1.22并配置VSCode(含GOPATH与Go Modules详解)
  • STM32CubeMX + HAL库实战:手把手教你用CAN总线控制RoboMaster M3508电机(附避坑点)
  • 终极Windows和Office永久激活指南:KMS_VL_ALL_AIO智能脚本完整教程
  • 别再乱删注册表了!Windows 10/11 下 MySQL 8.0.32 保姆级卸载与重装避坑指南
  • NotebookLM智能体插件开发:连接AI笔记与外部工具的实现指南
  • Oracle EBS 生产到成本解决方案(Production to Cost Solution) 及其各个阶段节点的会计分录核算
  • Bifrost:三星固件下载与管理的终极解决方案,让你轻松掌控设备升级
  • ChanlunX:通达信缠论分析的终极自动化解决方案
  • 家庭Wi-Fi vs 公司Wi-Fi:深入对比FAT AP、AC+FIT AP和云管理三种组网,教你按需选择
  • 浪潮NF5468M6服务器风扇太吵?手把手教你登录IBMC后台调低转速(附静音模式设置)
  • 从‘Hello DLL’到实战:用Qt动态库封装一个简易日志工具(附完整源码)
  • 大语言模型记忆增强框架:LightMem原理、实现与工程实践
  • SLO-Warden:基于错误预算的智能SLO守护平台设计与实践
  • 合宙BluePill开发板:9.9元ARM Cortex-M核心板硬件解析与实战指南
  • PANDA结果文件多到眼花?手把手教你解读FA、MD、网络矩阵等关键输出
  • 用Python和C++两种思路搞定NOI 1.5 20题:小球弹跳高度计算(附完整代码)
  • 别再只盯着Arduino了!用IPM模块驱动三相电机,从硬件选型到PCB布局的保姆级避坑指南
  • 告别风扇噪音烦恼!Fan Control:Windows上最智能的免费风扇控制软件完全指南
  • FPGA新手避坑指南:用Vivado IP核搞定AXI总线,从看懂波形开始
  • DayZ社区离线模式:5步搭建专属单人末日世界
  • BetaFlight硬件引脚资源管理:resource命令的实战配置与排错指南
  • 从零到一:用Microsoft To-Do构建高效个人任务管理体系