线程中断
Posted joe-go
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线程中断相关的知识,希望对你有一定的参考价值。
在Java中,线程中断是一种重要的线程协作机制,从表面上来看,中断就是让目标线程停止执行的意思,实际上却并非如此。
严格上讲,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出了。至于目标线程接到通知后如何处理,则完全由目标线程自行决定。这点很重要,如果中断后,线程立即无条件退出,这种方式太暴力了,可能会引起一些数据不一致的问题,线程的stop()方法就是因为这个原因被弃用。
与线程中断有关的方法有三个:
1.interrupt():
先看源码:
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag b.interrupt(this); return; } } interrupt0(); }
从注释就可以明显看出,interrupt()方法的作用就是“Just to set the interrupt flag”,即仅仅设置中断标识位。
2.isInterrupted():
先看源码:
public boolean isInterrupted() { return isInterrupted(false); }
这个方法通过检查中断标志位,判断当前线程是否被中断。
3.interrupted():
先看源码:
public static boolean interrupted() { return currentThread().isInterrupted(true); }
这是一个静态方法,也是用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位状态。也就是说,连续调用该方法两次,那么第二次的返回值一定是false。
下面举例说明这三个方法:
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run(){ while(true){ Thread.yield(); } } }; t1.start(); Thread.sleep(1000); t1.interrupt(); }
上面这个例子,虽然对t1进行了中断,但是运行后发现,线程t1并没有中断,由此可见,即使线程被设置上了中断状态,但是这个中断不会发生任何作用。
如果希望t1在中断后退出,就必须为它增加相应的中断处理代码,将上述例子修改为:
public static void main(String[] args) throws InterruptedException { Thread t1 = new Thread(){ @Override public void run(){ while(true){ if (Thread.currentThread().isInterrupted()) { System.out.println("t1 Interrupted!"); break; } Thread.yield(); } } }; t1.start(); Thread.sleep(1000); t1.interrupt(); }
输出结果:
t1 Interrupted!
表示线程中断成功。上述例子增加的部分 Thread.currentThread().isInterrupted() 判断当前线程是否被中断了,如果是,就退出循环体,结束线程。
思考:
存在这样的情况,当线程被阻塞的时候,比如被Object.wait()、Thread.join()或者Thread.sleep()这三种之一的方法阻塞时,该怎么中断线程呢?
有一种情况就是,线程在阻塞前恰好给自己设置了中断标识位,即调用了interrupt()方法,但是线程都阻塞了,当然也就不能通过 isInterrupt()方法来自己中断了;如果中断表示位都没有,就更不可能自己中断了。那么应该怎么办呢?
方法当然是有的,下面以sleep()方法为例,先来看看sleep()方法:
public static native void sleep(long millis) throws InterruptedException;
Thread.sleep()方法会让目标线程休眠若干时间,它会抛出一个 InterruptedException 中断异常。这个异常不是运行时异常,也就是说程序必须捕获并且处理它,当线程在sleep()时,如果被中断,这个异常就产生了。
看下面的例子:
public static void main(String[] args) throws InterruptedException { //将线程取名为t1 Thread t1 = new Thread("t1"){ @Override public void run(){ while(true){ if (Thread.currentThread().isInterrupted()) { System.out.println("t1 Interrupted!"); break; } try { System.out.println(Thread.currentThread().getName());//打印出当前线程的名字 System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位 System.out.println("开始睡觉"); Thread.sleep(2000); System.out.println("睡觉结束"); } catch (InterruptedException e) { System.out.println("Interrupted When Sleep!"); System.out.println(Thread.currentThread().isInterrupted());//查看当前线程的中断标识位 //设置中断状态 Thread.currentThread().interrupt(); } System.out.println("让出资源"); Thread.yield(); } } }; t1.start(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName());//打印出当前线程的名字 t1.interrupt(); System.out.println("000000"); }
输出结果:
t1 false 开始睡觉 main 000000 Interrupted When Sleep! false 让出资源 t1 Interrupted!
从输出结果来看,线程t1 start后,是没有中断标识位的,t1线程sleep 2秒,主线程main sleep了 1 秒,当t1还在sleep的时候,main线程执行了 t1.sleep(),所以t1是在sleep的时候被中断,由此捕获到异常,并设置了中断标识位,最后由break 退出中断线程。
注意:
Thread.sleep()方法由于中断而抛出异常,此时,它会清除中断标记,由上面输出结果可以看出在打印出中断后,中断标记位为false,如果不加上中断标识位,线程就不会中断,因此在异常处理中,再次设置上中断标识位。
参考: 《Java高并发程序设计》 葛一鸣 郭超 著
以上是关于线程中断的主要内容,如果未能解决你的问题,请参考以下文章
newCacheThreadPool()newFixedThreadPool()newScheduledThreadPool()newSingleThreadExecutor()自定义线程池(代码片段