1. InterruptedException异常概述
当一个方法后面声明可能会抛出InterruptedException 异常时,说明该方法是可能会花一点时间,但是可以取消的方法。
抛InterruptedException的代表方法有:
1. Java.lang.Object 类的 wait 方法
2. java.lang.Thread 类的 sleep 方法
3. java.lang.Thread 类的 join 方法
-- 需要花点时间的方法
执行wait方法的线程,会进入等待区等待被notify/notify All。在等待期间,线程不会活动。
执行sleep方法的线程,会暂停执行参数内所设置的时间。
执行join方法的线程,会等待到指定的线程结束为止。
因此,上面的方法都是需要花点时间的方法。
-- 可以取消的方法
因为需要花时间的操作会降低程序的响应性,所以可能会取消/中途放弃执行这个方法。
这里主要是通过interrupt方法来取消。
1. sleep方法与interrupt方法
interrupt方法是Thread类的实例方法,在执行的时候并不需要获取Thread实例的锁定,任何线程在任何时刻,都可以通过线程实例来调用其他线程的interrupt方法。
当在sleep中的线程被调用interrupt方法时,就会放弃暂停的状态,并抛出InterruptedException异常,这样一来,线程的控制权就交给了捕捉这个异常的catch块了。
2. wait方法和interrupt方法
当线程调用wait方法后,线程在进入等待区时,会把锁定接触。当对wait中的线程调用interrupt方法时,会先重新获取锁定,再抛出InterruptedException异常,获取锁定之前,无法抛出InterruptedException异常。
3. join方法和interrupt方法
当线程以join方法等待其他线程结束时,一样可以使用interrupt方法取消。因为join方法不需要获取锁定,故而与sleep一样,会马上跳到catch程序块
-- interrupt方法干了什么?
interrupt方法其实只是改变了中断状态而已。
而sleep、wait和join这些方法的内部会不断的检查中断状态的值,从而自己抛出InterruptEdException。
所以,如果在线程进行其他处理时,调用了它的interrupt方法,线程也不会抛出InterruptedException的,只有当线程走到了sleep, wait, join这些方法的时候,才会抛出InterruptedException。若是没有调用sleep, wait, join这些方法,或者没有在线程里自己检查中断状态,自己抛出InterruptedException,那InterruptedException是不会抛出来的。
isInterrupted方法,可以用来检查中断状态
Thread.interrupt方法,可以用来检查并清除中断状态,interrupt方法只能中断正在阻塞的线程而不对正在运行中的线程无作用。
2. 如何中断一个线程:
1. 使用共享变量(shared variable)发出信号,告诉线程必须停止正在运行的任务。线程必须周期性的核查这一变量(尤其在冗余操作期间),然后有秩序地中止任务。
package ThreadLocalTest;
public class TestCase extends Thread {
volatile boolean stop = false;
public static void main(String[] args) throws InterruptedException {
TestCase thread = new TestCase();
System.out.println("Starting thread...");
thread.start();
Thread.sleep(3000);
System.out.println("Asking thread to stop...");
thread.stop = true;
Thread.sleep(3000);
System.out.println("Stopping application...");
}
public void run() {
while (!stop) {
System.out.println("Thread is running...");
long time = System.currentTimeMillis();
while ((System.currentTimeMillis() - time < 1000) && (!stop)) {
}
}
System.out.println("Thread exiting under request...");
}
}
Starting thread...
Thread is running...
Thread is running...
Thread is running...
Thread is running...
Asking thread to stop...
Thread exiting under request...
Stopping application...
2. 对于有可能被阻塞的方法,正确的停止线程方式是设置共享变量,并调用interrupt()(注意变量应该先设置)。 如果线程没有被阻塞,这时调用interrupt()将不起作用;否则,线程就将得到异常(该线程必须事先预备好处理此状况),接着逃离阻塞状态。在任何一种情况中,最后线程都将检查共享变量然后再停止。Listing C这个示例描述了该技术。
package ThreadLocalTest;
public class ThreadBasic extends Thread {
volatile static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
ThreadBasic thread = new ThreadBasic();
System.out.println("Starting thread...");
thread.start();
Thread.sleep(1000);
System.out.println("Asking thread to stop...");
stop=true;
thread.interrupt();
Thread.sleep(3000);
System.out.println("Stopping application...");
}
public void run() {
while(!stop){
System.out.println("Thread running...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
System.out.println("Thread interrupted...");
}
System.out.println("Thread exiting under request...");
}
}
}
Starting thread...
Thread running...
Asking thread to stop...
Thread interrupted...
Thread exiting under request...
Stopping application...
3.如何处理InterruptedException异常
1. 不捕捉,将它传递给调用者
2. 捕捉后恢复中断状态
有时候抛出 InterruptedException
并不合适,例如当由 Runnable
定义的任务调用一个可中断的方法时,就是如此。在这种情况下,不能重新抛出InterruptedException
,但是您也不想什么都不做。当一个阻塞方法检测到中断并抛出InterruptedException
时,它清除中断状态。如果捕捉到 InterruptedException
但是不能重新抛出它,那么应该保留中断发生的证据,以便调用栈中更高层的代码能知道中断,并对中断作出响应。该任务可以通过调用 interrupt()
以 “重新中断” 当前线程来完成,如清单 3 所示。至少,每当捕捉到InterruptedException
并且不重新抛出它时,就在返回之前重新中断当前线程。
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();
}
}
}