那么不能直接把一个线程搞挂掉, 但有时候又有必要让一个线程死掉, 或者让它结束某种等待的状态 该怎么办呢? 优雅的方法就是, 给那个线程一个中断信号, 让它自己决定该怎么办. 比如说, 在某个子线程中为了等待一些特定条件的到来, 你调用了Thread.sleep(10000), 预期线程睡10秒之后自己醒来, 但是如果这个特定条件提前到来的话, 你怎么通知一个在睡觉的线程呢? 又比如说, 主线程通过调用子线程的join方法阻塞自己以等待子线程结束, 但是子线程运行过程中发现自己没办法在短时间内结束, 于是它需要想办法告诉主线程别等我了. 这些情况下, 就需要中断.
中断是通过调用Thread.interrupt()方法来做的. 这个方法通过修改了被调用线程的中断状态来告知那个线程, 说它被中断了. 对于非阻塞中的线程, 只是改变了中断状态, 即Thread.isInterrupted()将返回true; 对于可取消的阻塞状态中的线程, 比如等待在这些函数上的线程, Thread.sleep(), Object.wait(), Thread.join(), 这个线程收到中断信号后, 会抛出InterruptedException, 同时会把中断状态置回为false.
void | interrupt()
中断线程。(打一个中断标记)
|
static boolean | interrupted()
测试当前线程是否已经中断。
|
boolean | isInterrupted()
测试线程是否已经中断。
|
这样的话,线程被顺利的中断执行了。很多人实现一个线程类时,都会再加一个flag标记,以便控制线程停止执行,其实完全没必要,通过线程自身的中断状态,就可以完美实现该功能。如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 我们可以捕获该异常,并且做一些处理。另外,Thread.interrupted()方法是一个静态方法,它是判断当前线程的中断状态,需要注意的是,线程的中断状态会由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。
中断正在sleep的线程
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class InterruptTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(2000);
myThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
在主线程里启动子线程,子线程sleep 3s,然后主线程sleep 2s,醒来后中断子线程。因为子线程正在sleep,就像你累了在睡觉的时候,被老板打了一拳,醒来后( sleep时间结束)还傻傻的干事。但是 InterruptedException后会清除中断标志。
Thread-0
java.lang.InterruptedException: sleep interrupted
Thread-0
at java.lang.Thread.sleep(Native Method)
at MyThread.run(InterruptTest.java:13)
Thread-0
Thread-0
Thread-0
Thread-0
Thread-0
class MyThread extends Thread {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread().getName()+"isInterrupted="+Thread.currentThread().isInterrupted());
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("isInterrupted="+Thread.currentThread().isInterrupted());
break;
}
}
}
}
public class InterruptTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
try {
Thread.sleep(2000);
myThread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
子线程中断的时候遇到break,退出了循环,接受到中断异常时改变了中断状态
Thread-0isInterrupted=false
java.lang.InterruptedException: sleep interrupted
isInterrupted=false
at java.lang.Thread.sleep(Native Method)
at MyThread.run(InterruptTest.java:8)
子线程任务太多,自己中断自己
class MyThread extends Thread {
@Override
public void run() {
while (true) {
System.out.println(Thread.currentThread().getName()+"isInterrupted="+Thread.currentThread().isInterrupted());
try {
Thread.sleep(5000);
//自己任务太多,自己中断自己
Thread.currentThread().interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("isInterrupted="+Thread.currentThread().isInterrupted());
break;
}
}
}
}
public class InterruptTest {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
//主线程等待子线程完成任务
try {
myThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
System.out.println("子线程中断");
}
System.out.println(Thread.currentThread().getName());
}
}
Thread-0isInterrupted=false
Thread-0isInterrupted=true
java.lang.InterruptedException: sleep interrupted
isInterrupted=false
main
at java.lang.Thread.sleep(Native Method)
at MyThread.run(InterruptTest.java:9)
中断状态可以通过 Thread.isInterrupted()来读取,并且可以通过一个名为 Thread.interrupted()的静态方法读取和清除状态(即调用该方法结束之后, 中断状态会变成false)。
由于处于阻塞状态的线程 被中断后抛出exception并置回中断状态, 有时候是不利的, 因为这个中断状态可能会作为别的线程的判断条件, 所以稳妥的办法是在处理exception的地方把状态复位:
boolean interrupted = false;
try {
while (true) {
try {
return blockingQueue.take();
} catch (InterruptedException e) {
interrupted = true;
}
}
} finally {
if (interrupted){
Thread.currentThread().interrupt();
}
}
当代码调用中须要抛出一个InterruptedException, 你可以选择把中断状态复位, 也可以选择向外抛出InterruptedException, 由外层的调用者来决定.
不是所有的阻塞方法收到中断后都可以取消阻塞状态, 输入和输出流类会阻塞等待 I/O 完成,但是它们不抛出 InterruptedException,而且在被中断的情况下也不会退出阻塞状态.
尝试获取一个内部锁的操作(进入一个 synchronized 块)是不能被中断的,但是 ReentrantLock 支持可中断的获取模式即 tryLock(long time, TimeUnit unit)。
参考:http://blog.csdn.net/ghsau/article/details/17560467
http://blog.csdn.net/sunxing007/article/details/9123363