多线程 - 如何正确的终止线程?interrupt()、interrupted()、isInterrupted()区别?
总结
1 | java线程之间是协同式,不是抢占式 |
因为是协同式,所以线程之间都是“商量”着来,最佳实践是没有谁强迫谁终止的情况的,因此才会淘汰stop()方法,详见 多线程 - 为何要弃用stop() suspend()?(不过你硬要调用stop()就另当别论...)
- public void interrupt() 会为指定线程设置一个interrupted状态 -- 和stop()不同,不会强制线程停止,只会设置一个“interrupt标志位”,置为true(连续调用还是true,不会有异常发生。对比 Thread.start(), 多次调用会出现java.lang.IllegalStateException)。代表着“该线程被建议终止”。但是是否理会该建议,完全看该线程的具体逻辑是否去检查这个标志位,然后根据标志做出不同的反应。也有可能该线程的业务逻辑完全不理会标志位的变化,也就会继续我行我素的运行。
- public static boolean interrupted() 会判断当前线程的interrupted状态,并且会重置该状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。之后,该标志位会被清除。
- public boolean isInterrupted() 会判断指定线程的interrupted状态 -- 检查是否当前线程被设置了“interrupt标志位”。如果被设置了,返回true,反之返回false。但是,该函数不会清除标志位。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | @Test public void testInterrupted(){ boolean b1 = Thread.interrupted(); //false Thread.currentThread().interrupt(); boolean b2 = Thread.interrupted(); //true boolean b3 = Thread.interrupted(); //false } @Test public void testIsInterrupted(){ boolean b4 = Thread.currentThread().isInterrupted(); //false Thread.currentThread().interrupt(); boolean b5 = Thread.currentThread().isInterrupted(); //true boolean b6 = Thread.currentThread().isInterrupted(); //true } |
调用interrupt()抛出异常的情况(必看)
对于interrupt()方法,根据官方文档:
- 如果指定线程调用了interrupt(),并且该线程不处于被sleep,wait,join的状态,那么一切正常,不会抛出调用InterruptException异常,就不会清除中断标志位。如果此时再调用isInterrupted()就会返回true。
- 如果指定线程调用了interrupt()时,而指定线程正处于阻塞状态sleep,wait,join...线程自己就会立即抛InterruptException异常。
- 原本“interrupt标志位”应该是true(因为之前调用了interrupt()),也会被清除变为false。如果再调用isInterrupted()返回false。
- 如果你的程序不捕获这个异常,线程就会异常终止,进入TERMINATED状态;如果你的程序捕获了这个异常,那么程序就会继续执行catch语句块(可能还有finally语句块)以及以后的代码。
- 需要注意的是,InterruptedException是线程自己从内部抛出的,并不是interrupt()方法抛出的。对某一线程调用interrupt()时,如果该线程正在执行普通的代码,那么该线程根本就不会抛出InterruptedException。但是,一旦该线程进入到wait()/sleep()/join()后,就会立刻抛出InterruptedException。
Q:为什么当指定线程在被阻塞状态(sleep, wait)时,调用interrupt(),要立即抛出InterruptException异常,并清除“interrupt标志位”(从true变为false)
A:【本答案不是很严谨,请大家斧正】线程在被阻塞过程中,可能掌握着某种资源。如果线程刚从阻塞回来,“interrupt标志位”就是true,很可能(视用户自定义的代码而定)线程就真准备中断,没有给程序任何回收资源的时间。因此先取消标志位,回收下资源然后视情况再决定是否真的interrupt。
如果中断sleep,wait,join等,就会抛InterruptException异常,就会清除中断标志位,那么这种情况应该怎么处理呢?为了保证数据的一致性和完整性,我们需要用Thread.interrupt()方法再次中断自己,置上中断标志位。例子如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | @Test public void testInterrupt() throws InterruptedException { Thread t1 = new Thread() { public void run() { while ( true ) { if (Thread.currentThread().isInterrupted()) { System.out.println( "Interruted!" ); break ; } try { Thread.sleep( 2000 ); // 睡眠时中断会清除中断标志位 } catch (InterruptedException e) { // 如果少了下面这句,这个线程虽然在外面中断,但是只要中断睡眠中的进程 // 就会清除中断标志位,仍然处于无限循环,会竞争CPU资源 Thread.currentThread().interrupt(); // 再次中断置上中断标记 } Thread.yield(); } } }; t1.start(); //t1 thread would running Thread.sleep( 200 ); //sleep main thread t1.interrupt(); //after main thread wake up, would plan to interrupt t1 thread } |
interrupt()官方文档
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | 中断线程。 如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。 如果线程在调用 Object 类的 wait()、wait( long ) 或 wait( long , int ) 方法,或者该类的 join()、join( long )、join( long , int )、sleep( long ) 或 sleep( long , int ) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。 如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。 如果以前的条件都没有保存,则该线程的中断状态将被设置。 中断一个不处于活动状态的线程不需要任何作用。 抛出: SecurityException - 如果当前线程无法修改该线程 |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2019-03-07 SQL Server - case when...then...else...end