深入了解JAVA的线程中断方法经验之总结
今天是周六,周末到来之际送上一篇关于JAVA线程中断方式经验总结的福利。祝愿大家周末愉快!
一个多个线程在执行的Java程序,只有当其全部的线程执行结束时(更具体的说,是所有非守护线程结束或者某个线程调用System.exit()方法的时候),它才会结束运行。有时,你需要为了终止程序而结束一个线程,或者当程序的用户想要取消某个Thread对象正在做的任务。
Java提供中断机制来通知线程表明我们想要结束它。中断机制的特性是线程需要检查是否被中断,而且还可以决定是否响应结束的请求。所以,线程可以忽略中断请求并且继续运行。
首先我们先看一下
Thread和 Thread.currentThread有什么区别
Thread.currentThread 获取当前线程的实例。
Thread 是个线程类。
在JAVA中,曾经使用stop方法来停止线程,然而,该方法具有固有的不安全性,因而已经被抛弃(Deprecated)。那么应该怎么结束一个进程呢?官方文档中对此有详细说明:《为何不赞成使用 Thread.stop、Thread.suspend 和 Thread.resume?》。在此引用stop方法的说明:
Why is Thread.stop deprecated?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked. (The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
大概意思是: 因为该方法本质上是不安全的。停止一个线程将释放它已经锁定的所有监视器(作为沿堆栈向上传播的未检查 ThreadDeath 异常的一个自然后果)。
如果以前受这些监视器保护的任何对象都处于一种不一致的状态,则损坏的对象将对其他线程可见,这有可能导致任意的行为。此行为可能是微妙的,难以察觉,也可能是显著的。不像其他的未检查异常,ThreadDeath异常会在后台杀死线程,
因此,用户并不会得到警告,提示他的程序可能已损坏。这种损坏有可能在实际破坏发生之后的任何时间表现出来,也有可能在多小时甚至在未来的很多天后。
在文档中还提到,程序员不能通过捕获ThreadDeath异常来修复已破坏的对象。具体原因见原文。 既然stop方法不建议使用,那么应该用什么方法来代理stop已实现相应的功能呢?
先看下面的一张图:
1、通过修改共享变量来通知目标线程停止运行
大部分需要使用stop的地方应该使用这种方法来达到中断线程的目的。
这种方法有几个要求或注意事项: (1)目标线程必须有规律的检查变量,当该变量指示它应该停止运行时,该线程应该按一定的顺序从它执行的方法中返回。 (2)该变量必须定义为volatile,或者所有对它的访问必须同步(synchronized)。
例如: 假如你的applet包括start,stop,run几个方法:
你可以使用如下方式避免使用Thread.stop方法:
2、使用interrupt()中断线程
当一个线程运行时,另一个线程可以调用对应的Thread对象的interrupt()方法来中断它,该方法只是在目标线程中设置一个标志,表示它已经被中断,并立即返回。这里需要注意的是,如果只是单纯的调用interrupt()方法,线程并没有实际被中断,会继续往下执行。
演示休眠线程的中断
运行结果如下:
主线程启动新线程后,自身休眠2秒钟,允许新线程获得运行时间。新线程打印信息“about to sleep for 20 seconds”后,继而休眠20秒钟,大约2秒钟后,main线程通知新线程中断,那么新线程的20秒的休眠将被打断,从而抛出InterruptException异常,执行跳转到catch块,打印出“interrupted while sleeping”信息,并立即从run()方法返回,然后消亡,而不会打印出catch块后面的“leaving normally”信息。
请注意:由于不确定的线程规划,上图运行结果的后两行可能顺序相反,这取决于主线程和新线程哪个先消亡。但前两行信息的顺序必定如上图所示。
另外,如果将catch块中的return语句注释掉,则线程在抛出异常后,会继续往下执行,而不会被中断,从而会打印出”leaving normally“信息。
Thread 类还有其他可以检查线程是否被中断的方法。例如,这个静态方法interrupted()能检查正在运行的线程是否被中断。
isInterrupted()和interrupted() 方法有着很重要的区别。
第一个不会改变interrupted属性值,但是第二个会设置成false。
interrupted() 方法是一个静态方法,建议使用isInterrupted()方法。
待决中断
在上面的例子中,sleep()方法的实现检查到休眠线程被中断,它会相当友好地终止线程,并抛出InterruptedException异常。另外一种情况,如果线程在调用sleep()方法前被中断,那么该中断称为待决中断,它会在刚调用sleep()方法时,立即抛出InterruptedException异常。
如果PendingInterrupt不带任何命令行参数,那么线程不会被中断,最终输出的时间差距应该在2000附近(具体时间由系统决定,不精确),如果PendingInterrupt带有命令行参数,则调用中断当前线程的代码,但main线程仍然运行,最终输出的时间差距应该远小于2000,因为线程尚未休眠,便被中断,因此,一旦调用sleep()方法,会立即打印出catch块中的信息。执行结果如下:
这种模式下,main线程中断它自身。除了将中断标志(它是Thread的内部标志)设置为true外,没有其他任何影响。线程被中断了,但main线程仍然运行,main线程继续监视实时时钟,并进入try块,一旦调用sleep()方法,它就会注意到待决中断的存在,并抛出InterruptException。于是执行跳转到catch块,并打印出线程被中断的信息。最后,计算并打印出时间差。
使用isInterrupted()方法判断中断状态
可以在Thread对象上调用isInterrupted()方法来检查任何线程的中断状态。这里需要注意:线程一旦被中断,isInterrupted()方法便会返回true,而一旦sleep()方法抛出异常,它将清空中断标志,此时isInterrupted()方法将返回false。
下面的代码演示了isInterrupted()方法的使用:
运行结果如下:
使用Thread.interrupted()方法判断中断状态
可以使用Thread.interrupted()方法来检查当前线程的中断状态(并隐式重置为false)。又由于它是静态方法,因此不能在特定的线程上使用,而只能报告调用它的线程的中断状态,如果线程被中断,而且中断状态尚不清楚,那么,这个方法返回true。与isInterrupted()不同,它将自动重置中断状态为false,第二次调用Thread.interrupted()方法,总是返回false,除非中断了线程。
如下代码演示了Thread.interrupted()方法的使用:
运行结果
从结果中可以看出,当前线程中断自身后,在Y点,中断状态为true,并由Thread.interrupted()自动重置为false,那么下次调用该方法得到的结果便是false。
补充
yield和join方法的使用
join方法用线程对象调用,如果在一个线程A中调用另一个线程B的join方法,线程A将会等待线程B执行完毕后再执行。
yield可以直接用Thread类调用,yield让出CPU执行权给同等级的线程,如果没有相同级别的线程在等待CPU的执行权,则该线程继续执行。
JAVA多线程之中断机制(如何处理中断?)
阻塞(sleep等等)区别 中断(interrupt)+ 中断的意义
Java线程阻塞与中断
Hello,伙伴们
长按二维码就可以关注我们啦!