线程中断
定义
Java中的线程中断是一种线程间的协作模式,通过设置线程的中断标志并不能直接终止该线程的执行,而是被中断的线程根据中断状态自行处理。
方法
- void interrupt ()方法: 中断线程,例如当A线程运行时,线程B可以调用线程A的interrupt()方法来设置线程A的中断标志为 true 并立即返回。设置标志仅仅是设置标志,其实线程A并没有被中断,它还会继续往下执行。如果线程A因为调用了 wait() 、sleep()、jion()方法而被阻塞,这时候若B线程调用了A线程的interrupt() 方法,线程A会在调用这些方法的地方抛出InterruptedException异常而返回。
- boolean isInterrupted()方法: 检查当前线程是否被中断,如果是返回 true,否者返回 false
public boolean isInterrupt(){
//传递false,说明不清除中断标志
return isInterrupt(false);
}
- boolean interrupted()方法: 检测当前线程是否被中断,如果是返回true,否者返回false。与 isInterrupted() 方法不同的是,该方法如果发现当前线程被中断,则会清除中断标志,并且该方法是 static 方法,可以通过 Thread 直接调用,从它的源代码我们可以看到,在 interrupted() 方法内部是获取当前调用线程的中断标志,而不是调用 interrupted() 方法的实例对象的中断标志。
public boolean interrupted(){
//清除中断标志
return currentThread().isInterrupted(true);
}
举例
- 下面看一个使用interrupted() 方法优雅退出的经典例子
public void run(){
try{
...
while( !Thread.currentThread().isInterrupted() && more work to do ){
//do more work
}catch(InterruptedException e){
//这里是线程在阻塞期间然后又调用interrupt()方法之后抛出异常
}
finally{
//做其他的一些事
}
}
2、现在我们再来看一个根据中断标志来判断线程时候终止的例子
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
System.out.println(Thread.currentThread()+"hello!");
}
}
});
thread.start();
//让主线程休眠一秒,使中断前让子线程输出
Thread.sleep(1000);
System.out.println("main thread interrupt thread");
thread.interrupt();
thread.join();
System.out.println("main is over!!");
}
运行结果如下:
3、我们再来看一种情况,当线程为了等待一些特定的条件的到来时,一般会调用 sleep 方法、wait 方法或者 join 方法来阻塞挂起当前线程。比如一个线程调用了 sleep(3000),那么调用线程就会被阻塞3秒。但是有时候可能在3秒内条件已经满足了,如果一直等到3秒后返回有点浪费时间,这个时候我们可以调用线程的 interrupt() 方法来强制性的让sleep() 方法抛出 InterruptedException 异常而返回,让线程恢复到激活状态。
public static void main(String[] args) throws InterruptedException {
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
try {
System.out.println("线程开始休眠3000秒了");
Thread.sleep(3000000);
System.out.println("线程休眠好了");
} catch (InterruptedException e) {
System.out.println("线程休眠时调用了因interrupt()方法抛出异常了!!!");
}
System.out.println("线程被唤醒了");
}
});
thread.start();
//让主线程休眠一秒,确保子线程进入休眠
Thread.sleep(1000);
System.out.println("main thread interrupt thread");
thread.interrupt();
thread.join();
System.out.println("main thread is over!!");
}
运行结果:
interrupted() 与 isInterrupted() 方法的不同之处
奥义之处:通过他们两个方法的源码我们可以知道,interrupted() 方法它是检测当前线程是否被中断,如果是的话,则会清除中断标志,而 isInterrupted() 方法不会清除中断标志;而且 interrupted() 方法它是获取当前调用线程的中断标志,而不是调用 interrupted() 方法的实例对象的中断标志。(这里理解可能会比较饶)
打个比方来说:在我们一个类的mian方法里面我们创建了一个子线程 threadOne ,然后我们在main方法里面我们调用了 threadOne .interrupte() 方法来给 thread 线程设置中断标志,之后我们再在main方法里面调用 threadOne .isInterrupted()、threadOne .interrupted()、Thread.isInterrupted()、threadOne .isInterrupted() 这四个方法返回的结果将会是true、false、flase、true
注意:interrupted() 方法是一个static方法, isInterrupted() 方法是一个普通方法
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
for (; ; ) {
}
}
});
threadOne.start();
System.out.println("main thread interrupt thread");
Thread.currentThread().interrupt();//主线程设置了中断标志
System.out.println(threadOne.isInterrupted());
System.out.println(Thread.currentThread().isInterrupted());
System.out.println(threadOne.interrupted()); //这步清除了主线程的中断标志
System.out.println(Thread.currentThread().isInterrupted());
threadOne.join();
System.out.println("main thread is over!!");
}
运行结果:
== 改进上面的例子如下==
public static void main(String[] args) throws InterruptedException {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
while (!Thread.currentThread().interrupted()) {
}
System.out.println("threadOne isInterrupted:"+Thread.currentThread().isInterrupted());
}
});
threadOne.start();
System.out.println("main thread interrupt thread");
//设置中断标志
threadOne.interrupt();
threadOne.join();
System.out.println("main thread is over!!");
}
运行结果: