前言
前面在讲java中的线程中断机制中,我们提到了用stop和suspend来强制终止线程的不安全性,当时也提到了interrupt方法。这里再和大家一起学习下Java多线程中的interrupt,interrupted和isInterrupted。
interrupt的伸冤之路
首先对于interrupt方法,他是用于中断线程的,调用该方法的线程的状态将被置为"中断"状态。注意:线程中断仅仅是设置线程的中断状态位,不会停止线程。需要用户自己去监视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出InterruptedException的方法,比如说sleep,wait等方法)就是在监视线程的中断状态,一旦线程的中断状态被置为“中断状态”,就会抛出中断异常。
接下来,我们再来看看isInterrupted。isInterrupted是用来判断线程是否处于线程中断状态。如果是返回true,否则返回false。就如同下图中介绍的
下面我们来看个例子,看看interrupt如何被冤枉了
public class Interrupt {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Worker());
t.start();
Thread.sleep(200);
t.interrupt();
System.out.println("Main thread stopped.");
}
public static class Worker implements Runnable {
public void run() {
System.out.println("Worker started.");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
System.out.println("Worker IsInterrupted:" +
Thread.currentThread().isInterrupted());
}
System.out.println("Worker stopped.");
}
}
}
上面代码是主线程main启动了一个子线程Worker,然后让worker睡500ms,而main睡200ms,之后main调用worker线程的interrupt方法去中断worker,worker被中断后打印中断的状态。下面是执行结果:
Worker started.
Main thread stopped.
Worker IsInterrupted: false
Worker stopped.
看到上面的结果。估计认真看完上面对于interrupt和isInterrupted介绍的同学,一副问号脸,interrupt不是说好的修改线程为中断状态么,为啥isInterrupted返回的还是false?interrupt不要面子的?
稍安勿躁,其实上面的问题是出在了InterruptedException上面。我们先来看一段来自InterruptedException 的独白
InterruptedException - if any thread has interrupted the current thread. The interrupted status of the current thread is cleared when this exception is thrown.
原来是InterruptedException 这小子搞得鬼。由于调用了Thread.sleep(500)方法,而sleep()方法上面也说到会抛出InterruptedException 异常,这就导致了interrupt设定的中断状态被清除了,所以isInterrupted返回false就很好解释了。
interrupted和isInterrupted的恩恩怨怨
我们来仔细看看这两位的介绍,首先是interrupted
public static boolean interrupted() {
return currentThread().isInterrupted(true);
}
再是isInterrupted
public boolean isInterrupted() {
return isInterrupted(false);
}
这两个方法一个是static的,一个不是,但实际上都是在调用同一个方法,只是interrupted方法传入的参数为true,而inInterrupted传入的参数为false。所以要看清楚interrupted和isInterrupted的真实面目,我们还是要继续往下看
/**
* Tests if some Thread has been interrupted. The interrupted state
* is reset or not based on the value of ClearInterrupted that is
* passed.
*/
private native boolean isInterrupted(boolean ClearInterrupted);
这是一个native方法,看不到源码没有关系,参数名字ClearInterrupted已经清楚的表达了该参数的作用----是否清除中断状态。方法的注释也清晰的表达了“中断状态将会根据传入的ClearInterrupted参数值确定是否重置”。所以,静态方法interrupted将会清除中断状态(传入的参数ClearInterrupted为true),而实例方法isInterrupted则不会(传入的参数ClearInterrupted为false)。
为了验证上面的区别,我们接着看下面的例子
public class Interrupt {
public static void main(String[] args) throws Exception {
Thread t = new Thread(new Worker());
t.start();
Thread.sleep(200);
t.interrupt();
System.out.println("Main thread stopped.");
}
public static class Worker implements Runnable {
public void run() {
System.out.println("Worker started.");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread curr = Thread.currentThread();
//再次调用interrupt方法中断自己,将中断状态设置为“中断”
curr.interrupt();
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Static Call: " + Thread.interrupted());//clear status
System.out.println("---------After Interrupt Status Cleared----------");
System.out.println("Static Call: " + Thread.interrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
System.out.println("Worker IsInterrupted: " + curr.isInterrupted());
}
System.out.println("Worker stopped.");
}
}
}
执行结果:
Worker started.
Main thread stopped.
Worker IsInterrupted: true
Worker IsInterrupted: true
Static Call: true
---------After Interrupt Status Cleared----------
Static Call: false
Worker IsInterrupted: false
Worker IsInterrupted: false
Worker stopped.
从执行结果也可以看到,前两次调用isInterrupted方法都返回true,说明isInterrupted方法不会改变线程的中断状态,而接下来调用静态的interrupted()方法,第一次返回了true,表示线程被中断,第二次则返回了false,因为第一次调用的时候已经清除了中断状态。最后两次调用isInterrupted()方法就肯定返回false了。
上面的代码中我们看到,我们在catch块中重新调用了interrupt方法,重置了中断状态,所以后面的isInterrupt方法才会返回true。那么我们在什么情况下需要在catch块中去重置中断状态呢?其实如果我们的方法中不能抛出InterruptedException(就像这里的Thread.sleep语句放在了Runnable的run方法中,这个方法不允许抛出任何受检查的异常),但又想告诉上层调用者这里发生了中断的时候,就只能在catch里面重置中断状态了。就如同下面的例子
public class TaskRunner implements Runnable {
private BlockingQueue<Task> queue;
public TaskRunner(BlockingQueue<Task> queue) {
this.queue = queue;
}
public void run() {
try {
while (true) {
Task task = queue.take(10, TimeUnit.SECONDS);
task.execute();
}
} catch (InterruptedException e) {
// Restore the interrupted status
Thread.currentThread().interrupt();
}
}
}
总结
上面介绍了interrupt,interrupted和isInterrupt方法在线程中断的区别。只为大家更好的理解这三者的关系和区别。本文参考(借鉴)了 interrupt、interrupted和isInterrupted的区别 的内容,我用自己的语言稍微组织了下,便于自己理解学习。