本文共 3711 字,大约阅读时间需要 12 分钟。
本文隶属于专栏《100个问题搞定Java并发》,该专栏为笔者原创,引用请注明来源,不足和错误之处请在评论区帮忙指出,谢谢!
本专栏目录结构和参考文献请见
在Java中,线程中断是一种重要的线程协作机制。从表面上理解,中断就是让目标线程停止执行的意思,实际上并非完全如此。
在专栏前面我们提到了 stop() 方法停止线程的坏处,详情请见——
在 JDK 中是否有提供完善的支持线程退出的能力呢?答案是肯定的,那就是线程中断。
严格地讲,线程中断并不会使线程立即退出,而是给线程发送一个通知,告知目标线程,有人希望你退出啦!
至于目标线程接到通知后如何处理,则完全由目标线程自行决定这点很重要,如果中断后,线程立即无条件退出,我们就又会遇到 stop 方法的老问题。
有三个方法与线程中断有关,这三个方法看起来很像,可能会引起混淆和误用,希望大家注意。
这三个方法都来自于 java.lang.Thread
public void interrupt(); //中断线程public boolean isInterrupted(); //判断当前线程是否已经被中断public static boolean interrupted(); //判断当前线程是否已经被中断,并且清除当前中断状态
Thread.interrupt() 方法是一个实例方法。 它通知目标线程中断,也就是设置中断标志位。 中断标志位表示当前线程已经被中断了。
Thread.isInterrupted() 方法也是实例方法,它判断当前线程是否被中断(通过检査中断标志位)。
最后的静态方法 Thread.interrupted() 也可用来判断当前线程的中断状态,但同时会清除当前线程的中断标志位状态。
package com.shockang.study.java.concurrent.thread.interrupt;/** * sleep中断后 抛出异常被重置中断状态 * 如果希望sleep后可以判断中断状态,则必须在sleep的异常处理中,在设置中断 * * @author Administrator */public class InterruptSleepThread { 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("Interrupted!"); break; } try { Thread.sleep(2000); // 推荐使用 TimeUnit.MILLISECONDS.sleep(2000); } catch (InterruptedException e) { System.out.println("Interrupted When Sleep"); //设置中断状态 Thread.currentThread().interrupt(); } Thread.yield(); } } }; t1.start(); Thread.sleep(2000); t1.interrupt(); }}
在 catch 子句部分,由于已经捕获了中断,我们可以立即退出线程。
但在这里,我们并没有这么做,因为也许在这段代码中,我们还必须进行后续的处理来保证数据的一致性和完整性,
因此,执行了 Thread.interrupt 方法再次中断自己,置上中断标记位。
只有这么做,在前面的中断检査中,才能发现当前线程已经被中断了。
注意: Thread.sleep() 方法由于中断而抛出异常,此时,它会清除中断标记,如果不加处理,那么在下一次循环开始时,就无法捕获这个中断,故在异常处理中,再次设置中断标记位。
/** * 中断此线程。 * * 除非当前线程正在中断自身(这是始终允许的),否则将调用此线程的checkAccess方法,这可能会导致抛出SecurityException。 * * 如果此线程在调用对象类的 wait()、wait(long) 或wait(long, int) 方法时被阻塞, * * 或者在调用此类的join()、join(long)、join(long, int)、sleep(long) 或sleep(long, int)方法时被阻塞, * * 则其中断状态将被清除,并且它将接收到InterruptedException。 * * 如果此线程在I/O操作中被阻塞在中断通道上,则通道将被关闭,线程的中断状态将被设置,并且线程将接收java.nio.channels.ClosedByInterruptException。 * * 如果该线程在java.nio.channels.Selector中被阻塞,那么该线程的中断状态将被设置,并且它将立即从选择操作返回,可能返回一个非零值,就像调用了选择器的唤醒方法一样。 * * 如果前面的条件都不成立,那么这个线程的中断状态将被设置。 * * 中断一个非活动线程不需要有任何效果。 * * @throws SecurityException–如果当前线程无法修改此线程 * */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();}
/** * 测试此线程是否已中断。 * * 线程的中断状态不受此方法的影响。 * * 由于线程在中断时不活动而被忽略的线程中断将由返回false的方法反映。 * * @return 如果此线程已中断,则为true;否则为假。 */public boolean isInterrupted() { return isInterrupted(false);}/** * 测试某个线程是否被中断。中断状态是否重置取决于传递的ClearInterrupted的值 */private native boolean isInterrupted(boolean ClearInterrupted);
/** * 测试当前线程是否已中断。 * * 此方法清除线程的中断状态。 * * 换句话说,如果这个方法被连续调用两次,那么第二个调用将返回false * * (除非当前线程在第一个调用清除其中断状态之后,第二个调用检查它之前再次中断)。 * * 由于线程在中断时不活跃而被忽略的线程中断,将由这个方法返回false * * @return true 当前线程成功被中断 * false 当前线程未被中断 */public static boolean interrupted() { return currentThread().isInterrupted(true);}
转载地址:http://wugji.baihongyu.com/