Java中断机制是一种协做机制,也就是说经过中断并不能直接终止另外一个线程,它只是要求被中断线程在合适的时机中断本身,这须要被中断的线程本身处理中断。这比如是家里的父母叮嘱在外的子女要注意身体,但子女是否注意身体,怎么注意身体则彻底取决于本身。html
通常说来,当可能阻塞的方法声明中有抛出InterruptedException则暗示该方法是可中断的,如BlockingQueue#put、BlockingQueue#take、Object#wait、Thread#sleep、condition.await以及可中断的通道上的 I/O 操做方法等,若是程序捕获到这些可中断的阻塞方法抛出的InterruptedException或检测到中断后,这些中断信息该如何处理?通常有如下两个通用原则:
若是遇到的是可中断的阻塞方法抛出InterruptedException,能够继续向方法调用栈的上层抛出该异常,若是是检测到中断,则可清除中断状态并抛出InterruptedException,使当前方法也成为一个可中断的方法。
如有时候不太方便在方法上抛出InterruptedException,好比要实现的某个接口中的方法签名上没有throws InterruptedException,这时就能够捕获可中断方法的InterruptedException并经过Thread.currentThread.interrupt()来从新设置中断状态。若是是检测并清除了中断状态,亦是如此。
通常的代码中,尤为是做为一个基础类库时,毫不应当吞掉中断,即捕获到InterruptedException后在catch里什么也不作,清除中断状态后又不重设中断状态也不抛出InterruptedException等。由于吞掉中断状态会致使方法调用栈的上层得不到这些信息。
固然,凡事总有例外的时候,当你彻底清楚本身的方法会被谁调用,而调用者也不会由于中断被吞掉了而遇到麻烦,就能够这么作。
总得来讲,就是要让方法调用栈的上层获知中断的发生。假设你写了一个类库,类库里有个方法amethod,在amethod中检测并清除了中断状态,而没有抛出InterruptedException,做为amethod的用户来讲,他并不知道里面的细节,若是用户在调用amethod后也要使用中断来作些事情,那么在调用amethod以后他将永远也检测不到中断了,由于中断信息已经被amethod清除掉了。若是做为用户,遇到这样有问题的类库,又不能修改代码,那该怎么处理?只好在本身的类里设置一个本身的中断状态,在调用interrupt方法的时候,同时设置该状态,这实在是无路可走时才使用的方法。
注意:synchronized在获锁的过程当中是不能被中断的,意思是说若是产生了死锁,则不可能被中断(请参考后面的测试例子)。与synchronized功能类似的reentrantLock.lock()方法也是同样,它也不可中断的,即若是发生死锁,那么reentrantLock.lock()方法没法终止,若是调用时被阻塞,则它一直阻塞到它获取到锁为止。可是若是调用带超时的tryLock方法reentrantLock.tryLock(long timeout, TimeUnit unit),那么若是线程在等待时被中断,将抛出一个InterruptedException异常,这是一个很是有用的特性,由于它容许程序打破死锁。你也能够调用reentrantLock.lockInterruptibly()方法,它就至关于一个超时设为无限的tryLock方法。java
interrupt()不能中断在运行中的线程,它只能改变中断状态而已。异步
即上面状态图中的:“等待Blocked”-->“锁定Blocked”ide
一、interrupt()
interrupt方法用于中断线程。调用该方法的线程的状态为将被置为"中断"状态。
注意:线程中断仅仅是置线程的中断状态位,不会中止线程。须要用户本身去监视线程的状态为并作处理。支持线程中断的方法(也就是线程中断后会抛出interruptedException的方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。
性能
二、interrupted()测试
public static boolean interrupted() { return currentThread().isInterrupted(true); }
该方法就是直接调用当前线程的isInterrupted(true)的方法。静态方法interrupted会将当前线程的中断状态清除,但这个方法的命名极不直观,很容易形成误解,须要特别注意。
三、isInterrupted()this
public boolean isInterrupted() { return isInterrupted(false); } private native boolean isInterrupted(boolean flag);
该方法却直接调用当前线程的native isInterrupted(false)的方法,不清除中断状态。spa
所以interrupted()和isInterrupted()这两个方法主要区别:这两个方法最终都会调用同一个方法-----isInterrupted( Boolean 参数)(重载方法,是私有的native方法),只不过参数固定为一个是true,一个是false;线程
因为第二个区别主要体如今调用的方法的参数上,让咱们来看一看这个参数是什么含义:
先来看一看被调用的方法 isInterrupted(boolean arg)(Thread类中重载的方法)的定义:code
private native boolean isInterrupted( boolean flag);
这个本地方法,看不到源码。做用是要清除状态位。若是这个参数为true,说明返回线程的状态位后,要清掉原来的状态位(恢复成原来状况)。这个参数为false,就是直接返回线程的状态位。
这两个方法很好区分,只有当前线程才能清除本身的中断位(对应interrupted()方法)
interrupt()只是改变中断状态而已,interrupt()不会中断(不是中止线程)一个正在运行的线程。这一方法实际上完成的是,给线程抛出一个中断信号, 这样受阻线程就得以退出阻塞的状态(不会中止线程。须要用户本身去监视线程的状态为并作处理)。更确切的说,若是线程被Object.wait, Thread.join和Thread.sleep三种方法之一阻塞,那么,它将接收到一个中断异常(InterruptedException),从而提前地终结被阻塞状态。
或者能够简单总结一下中断两种状况:
例如:
线程A在执行sleep,wait,join时,线程B调用线程A的interrupt方法,的确这一个时候A会有InterruptedException 异常抛出来。 但这实际上是在sleep,wait,join这些方法内部会不断检查中断状态的值,而本身抛出的InterruptedException。
若是线程A正在执行一些指定的操做时如赋值,for,while,if,调用方法等,都不会去检查中断状态,因此线程A不会抛出 InterruptedException,而会一直执行着本身的操做。当线程A终于执行到wait(),sleep(),join()时,才立刻会抛出 InterruptedException.
若没有调用sleep(),wait(),join()这些方法,即没有在线程里本身检查中断状态本身抛出InterruptedException的话,那InterruptedException是不会被抛出来的。
注意1:当线程A执行到wait(),sleep(),join()时,抛出InterruptedException后,中断状态已经被系统复位了,线程A调用Thread.interrupted()返回的是false。
示例:interrupt()不能中断在运行中的线程,它只能改变中断状态而已。
package com.dxz; public class InterruptionInJava implements Runnable { public static void main(String[] args) throws InterruptedException { Thread testThread = new Thread(new InterruptionInJava(), "InterruptionInJava"); // start thread testThread.start(); Thread.sleep(1000); // interrupt thread testThread.interrupt(); System.out.println("main end"); } @Override public void run() { while (true) { if (Thread.currentThread().isInterrupted()) { System.out.println("Yes,I am interruted,but I am still running"); } else { System.out.println("not yet interrupted"); } } } }
结果:
interrupt()不能中断在运行中的线程,它只能改变中断状态而已,那么,如何正确中断?既然是只能修改中断状态,那么咱们应该针对中断状态作些什么?
修改代码,在状态判断中如上,添加一个return就ok了。但现实中,咱们可能须要作的更通用,不由又要发出天问,如何中断线程?答案是添加一个开关。
//... if(Thread.currentThread().isInterrupted()){ System.out.println("Yes,I am interruted,but I am still running"); return; //...
线程A正在使用sleep()暂停着: Thread.sleep(100000);
若是要取消他的等待状态,能够在正在执行的线程里(好比这里是B)调用。
a.interrupt();
令线程A放弃睡眠操做,这里a是线程A对应到的Thread实例
当在sleep中时 线程被调用interrupt()时,就立刻会放弃暂停的状态.并抛出InterruptedException.丢出异常的,是A线程。
package com.dxz.interrupt; public class ThreadDemo1A extends Thread { @Override public void run() { try { System.out.println("线程a开始工做"); sleep(30000); } catch (InterruptedException e) { System.out.println(this.isInterrupted()); } } } package com.dxz.interrupt; import java.util.concurrent.TimeUnit; public class ThreadDemo1B { public static void main(String[] args) throws InterruptedException { ThreadDemo1A threada = new ThreadDemo1A(); threada.start(); TimeUnit.SECONDS.sleep(3); System.out.println("开始中断线程a"); threada.interrupt(); } }
结果:
线程a开始工做 开始中断线程a false
线程A调用了wait()进入了等待状态,也能够用interrupt()取消。
不过这时候要当心锁定的问题.线程在进入等待区,会把锁定解除,当对等待中的线程调用interrupt()时,会先从新获取锁定,再抛出异常。在获取锁定以前,是没法抛出异常的。
package com.dxz.interrupt; public class ThreadDemo2A extends Thread { @Override public void run() { try { System.out.println("线程a开始工做"); synchronized (this) { wait(30000); } } catch (InterruptedException e) { System.out.println(this.isInterrupted()); } } } package com.dxz.interrupt; import java.util.concurrent.TimeUnit; public class ThreadDemo2B { public static void main(String[] args) throws InterruptedException { ThreadDemo2A threada = new ThreadDemo2A(); threada.start(); TimeUnit.SECONDS.sleep(3); System.out.println("开始中断线程a"); threada.interrupt(); } }
结果:
线程a开始工做 开始中断线程a false
当线程以join()等待其余线程结束时,当它被调用interrupt(),它与sleep()时同样, 会立刻跳到catch块里。注意,是对谁调用interrupt()方法,必定是调用被阻塞线程的interrupt方法.如在线程a中调用来线程t.join(),
则a会等t执行完后在执行t.join后的代码,当在线程b中调用a.interrupt()方法,则会抛出InterruptedException,固然线程t的join()也就被取消了。线程t还会继续运行下去。
package com.dxz.interrupt; public class T extends Thread { public T(String s) { super(s); } @Override public void run() { System.out.println("线程t开始进入死循环"); for(;;); } } package com.dxz.interrupt; public class ThreadDemo3A extends Thread { public ThreadDemo3A(String s) { super(s); } @Override public void run() { try { System.out.println("线程a等待线程t执行"); T t = new T("demo3t"); t.start(); t.join(); System.out.println("线程a中的线程t已经结束"); } catch (InterruptedException e) { System.out.println(this.isInterrupted()); } } } package com.dxz.interrupt; import java.util.concurrent.TimeUnit; public class ThreadDemo3B { public static void main(String[] args) { ThreadDemo3A threada = new ThreadDemo3A("demo3a"); threada.start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("开始中断线程a"); threada.interrupt(); System.out.println("--------"); } }
结果:
线程a等待线程t执行 线程t开始进入死循环 开始中断线程a -------- false
能够看到T线程在主线程结束了,还在运行。
package com.dxz.synchronize; import java.util.concurrent.TimeUnit; public class InterruputThread { public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread() { @Override public void run() { while (true) { // 判断当前线程是否被中断 if (this.isInterrupted()) { System.out.println("线程中断"); break; } } System.out.println("已跳出循环,线程中断!"); } }; t1.start(); TimeUnit.SECONDS.sleep(2); t1.interrupt(); } }
结果:
线程中断
已跳出循环,线程中断!
此外,类库中的有些类的方法也可能会调用中断,如FutureTask中的cancel方法,若是传入的参数为true,它将会在正在运行异步任务的线程上调用interrupt方法,若是正在执行的异步任务中的代码没有对中断作出响应,那么cancel方法中的参数将不会起到什么效果;又如ThreadPoolExecutor中的shutdownNow方法会遍历线程池中的工做线程并调用线程的interrupt方法来中断线程,因此若是工做线程中正在执行的任务没有对中断作出响应,任务将一直执行直到正常结束。
判断某个线程是否已被发送过中断请求,请使用Thread.currentThread().isInterrupted()方法(由于它将线程中断标示位设置为true后,不会马上清除中断标示位,即不会将中断标设置为false),而不要使用thread.interrupted()(该方法调用后会将中断标示位清除,即从新设置为false)方法来判断,下面是线程在循环中时的中断方式:
while(!Thread.currentThread().isInterrupted() && more work to do){
do more work
}
显然,做为一种协做机制,不会强求被中断线程必定要在某个点进行处理。实际上,被中断线程只需在合适的时候处理便可,若是没有合适的时间点,甚至能够不处理,这时候在任务处理层面,就跟没有调用中断方法同样。“合适的时候”与线程正在处理的业务逻辑紧密相关,例如,每次迭代的时候,进入一个可能阻塞且没法中断的方法以前等,但多半不会出如今某个临界区更新另外一个对象状态的时候,由于这可能会致使对象处于不一致状态。
处理时机决定着程序的效率与中断响应的灵敏性。频繁的检查中断状态可能会使程序执行效率降低,相反,检查的较少可能使中断请求得不到及时响应。若是发出中断请求以后,被中断的线程继续执行一段时间不会给系统带来灾难,那么就能够将中断处理放到方便检查中断,同时又能从必定程度上保证响应灵敏度的地方。当程序的性能指标比较关键时,可能须要创建一个测试模型来分析最佳的中断检测点,以平衡性能和响应灵敏性。
处理方式
程序里发现中断后该怎么响应?这就得视实际状况而定了。有些程序可能一检测到中断就立马将线程终止,有些多是退出当前执行的任务,继续执行下一个任务……做为一种协做机制,这要与中断方协商好,当调用interrupt会发生些什么都是事先知道的,如作一些事务回滚操做,一些清理工做,一些补偿操做等。若不肯定调用某个线程的interrupt后该线程会作出什么样的响应,那就不该当中断该线程。
Thread.stop方法已经不推荐使用了。而在某些方面Thread.stop与中断机制有着类似之处。如当线程在等待内置锁或IO时,stop跟interrupt同样,不会停止这些操做;当catch住stop致使的异常时,程序也能够继续执行,虽然stop本意是要中止线程,这么作会让程序行为变得更加混乱。
那么它们的区别在哪里?最重要的就是中断须要程序本身去检测而后作相应的处理,而Thread.stop会直接在代码执行过程当中抛出ThreadDeath错误,这是一个java.lang.Error的子类。