您的位置:首页 > 编程语言 > Java开发

你真的了解Java中的interrupt()中断线程吗?

2017-05-11 12:47 489 查看
很多Java初级开发者(包括我)都知道终止一个正在运行的线程最好的方法不是

JDK已废弃的stop()方法,而是用interrupt()或条件变量,但事实上真就那么简单吗?

很多东西一旦涉及到多线程,问题就复杂起来,你必须考虑很多问题。

今天我们就好好聊聊Java中的一个interrupt()中断线程方法,同时涉及到了很多其

他问题(都是坑啊,一不小心就进去了)。

中断可以理解为线程的一个标志位属性,它表示一个运行中的线程是否被其他线程

进行了中断操作。中断好比其它线程对该线程打了个招呼,其它线程通过调用该线程的

interrupt()方法对其进行中断操作,相当于将该线程的中断标志位设置为true,而被中

断的线程自身通过检查中断标志位做出中断响应,说白了,就是我们程序员自己写中断

处理程序的代码(因为JDK的开发者并不知道我们中断线程后如何处理未完成的任务)。

这里演示一段代码,表明单纯用interrupt()中断线程方法并不能停止当前正在运行的线

程,需要配合其它方法才能正确停止线程。

public class InterruptDemo {

static class MyRunnable implements Runnable{

public void run() {
for(int i=0;i<50000;i++)
{
System.out.println("i="+(i+1));

}
}

}
public static void main(String[] args) {
Thread t=new Thread(new MyRunnable());
t.start();
t.interrupt();
}

}


看控制台输出结果:



可见,线程一直打印到50000,执行完毕后退出线程,并没有我们预料中在某处中断。

结论:单纯用interrupt()中断线程方法并不能停止当前正在运行的线程,需要配合

其它方法才能正确停止线程。

这里的其它方法包括:interrupted()与isInterrupted(),两者都是判断当前线程

的中断标志位是否为true,两者都是唯一区别是,前者会读取并清除中断状态,后者仅读取状态。意思是,假如当前线程已经被调用interrupt()方法将中断标志位设为true,那么在调用interrupted()方法首先会返回一个true,然后将其中断标志位设为false,而如果调用isInterrupted()方法,始终会返回true,其中断标志位不变。程序中建议慎用interrupted()方法,而使用isInterrupted()方法,不过应根据业务需求和底层原理灵活使用,这就考虑程序员的能力了。下面配合isInterrupted()方法完成对上面代码的改进,让其在某处中断下来。

public class InterruptDemo2 {

static class MyRunnable implements Runnable{

public void run() {
for(int i=0;!Thread.currentThread().isInterrupted()&&i<50000;i++)
{
System.out.println("i="+(i+1));
}
}

}
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new MyRunnable());
t.start();
Thread.currentThread().sleep(5);//让线程运行一段时间
t.interrupt();
t.join();//等待指定的线程终止
System.out.println("end");
}


看控制台输出结果:



这里完成了一个简单的中断处理程序。但令人困惑的还不止这两个方法,我们在继

续深入下去,最后给出Java中断线程的最佳实践和模板代码。

当一个正在运行的线程遇到阻塞时,中断该线程的情况将变得更加复杂和麻烦,这

里为方便起见,仅举出遇到wait/sleep/join阻塞后,调用interrupt()方法的情况。

查阅JDK文档,可知:如果该线程正阻塞于Object类的wait()、wait(long)、

wait(long, int)方法,或者Thread类的join()、join(long)、join(long, int)、sleep(long)、sleep(long, int)方法,则该线程的中断状态将被清除(即中断标志位设为false),并收到一个java.lang.InterruptedException。

因此,(画重点,敲黑板!)在编写多线程代码的时候,任何时候捕获到 

InterruptedException,要么继续上抛,要么重置中断状态,这是最安全的做法.

模板代码:

public void run() {
try {
// ① 调用阻塞方法
} catch (InterruptedException e) {
Thread.currentThread().interrupt();    // ② 恢复被中断的状态
}
}


这里举个例子:

public class InterruptDemo3 {

static class MyRunnable implements Runnable{

public void run() {
while(!Thread.currentThread().isInterrupted()){
try {
System.out.println("sleep begin!");
Thread.currentThread().sleep(1000);
System.out.println("sleep end!");
} catch (InterruptedException e) {
System.out.println("睡眠中遇中断进入catch,重置中断标志位,退出循环!");
Thread.currentThread().interrupt();
}

}
System.out.println("中断后正常退出");

}

}
public static void main(String[] args) throws InterruptedException {
Thread t=new Thread(new MyRunnable());
t.start();
Thread.currentThread().sleep(1000);//让线程t运行一段时间
t.interrupt();
}

}


看控制台输出结果:



假如将13行注释起来,线程将无法中断,一直循环下去。

所以说,线程中断还真不是一个简单的问题,中断后的处理程序要程序员自己写,

一定要慎之又慎。在工作中必须灵活运用,知道原理和处理方法。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: