Java 中断线程(interrupt)
@author:Jingdai
@date:2020.10.06
概念
当
A
线程想让B
线程终止运行,应该怎么办呢?在Java之前的版本中,可以利用stop
方法来使一个线程终止,但是该方法已经被废弃了,不要这么用。现在可以在
A
线程中调用B
线程的interrupt()
方法,来使B
线程知道有线程想要使自己终止,但是是否终止取决于B
线程自己,B
完全可以不理会这个终止请求。(当然最好不要这么做)下面我们来看看细节。
相关函数介绍
void interrupt()
中断这个线程。
boolean isInterrupted()
检查这个线程是否被中断。
static boolean interrupted()
检查当前线程是否被中断,该方法在调用后还会清除该线程的中断状态。
示例解释
boolean isInterrupted()
方法和static boolean interrupted()
方法区别如下代码首先测试
boolean isInterrupted()
方法。public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println(Thread.currentThread().isInterrupted()); // true System.out.println(Thread.currentThread().isInterrupted()); // true }
会发现两次输出都是
true
。接着测试
static boolean interrupted()
方法。public static void main(String[] args) { Thread.currentThread().interrupt(); System.out.println(Thread.interrupted()); // true System.out.println(Thread.interrupted()); // false }
测试发现第一次输出是 true ,而第二次输出是 false ,说明这个方法在调用后会清除线程的中断状态。
中断且不理会
这里我们让主线程中断线程
aThread
,但是aThread
线程不理会,看会发生什么。代码如下:public static void main(String[] args){ // create and start aThread Runnable r = () -> { while (true) { System.out.println("I'm alive"); } }; Thread aThread = new Thread(r); aThread.start(); // interrupt aThread aThread.interrupt(); }
测试发现
aThread
线程的运行没有任何影响,还是一直输出I'm alive
。一般用法
既然如果中断不被处理的话不会有任何效果,按应该怎么响应中断呢?应该在线程中一直判断线程是否被中断,如果线程被中断,应该根据当前任务的要求选择接下来的工作,在结束线程前关闭必要的资源。看下面的代码。
public static void main(String[] args){ // create and start aThread Runnable r = () -> { try { while (!Thread.currentThread().isInterrupted()) { //do work System.out.println("I'm alive"); } System.out.println("I ll die"); } finally { // close some resources } }; Thread aThread = new Thread(r); aThread.start(); try { Thread.currentThread().sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // interrupt aThread aThread.interrupt(); }
线程在没有被中断时,一直执行自己的任务,当被中断时,就会跳出循环,并在
finally
中关闭相应的资源和并做一些清理操作。线程被阻塞时
上面的情况在线程没有被阻塞时可以运行,但是当线程被阻塞时,线程是无法检查线程的中断状态的,所以引入了
InterruptedException
异常帮助中断阻塞。不同的阻塞还有一些区别,具体如下。
线程被
sleep()
方法或join()
方法阻塞时如下代码:
public static void main(String[] args) { // create and start aThread Runnable r = () -> { try { Thread.currentThread().sleep(5000); } catch (InterruptedException e) { System.out.println("die"); e.printStackTrace(); } finally { // close some resources } }; Thread aThread = new Thread(r); aThread.start(); try { Thread.currentThread().sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } // interrupt aThread aThread.interrupt(); }
在这些线程上调用
interrupt()
方法时,sleep()/join()
方法会被中断不再执行,它会清除中断状态并直接跳入catch
块中执行,所以如果在循环中调用了sleep()
方法,就不要去检测中断状态了,因为会直接跳入catch
块中执行,应该直接捕获InterruptedException
异常。线程被
wait()
方法阻塞时线程被
wait()
方法阻塞时,调用interrupt()
方法的效果和线程被sleep()
方法或join()
方法阻塞的效果基本一样,它也会清除中断状态并跳入catch
块中去执行。但是有一点点不同,就是被interrupt()
调用的线程会先去获取调用wait()
方法的对象的锁,获取完对象的锁之后才会抛出异常并进入catch
块中去执行,如果获得不到锁,那么就无法抛出异常,也无法进入catch
块中。如下代码,首先aThread
获得锁并wait
(注意wait()
方法会释放锁),然后bThead
获得锁,但是b
永远不释放锁,在aThread
wait
的时候,主线程调用interrupt()
去中断aThread
,但是由于aThread
永远无法获得锁,所以它不会抛出异常,也不会进入到catch
块中运行。代码如下。public static void main(String[] args) { Runnable r = () -> { try { synchronized (o) { System.out.println("aThread get lock"); o.wait(10000); } } catch (InterruptedException e) { e.printStackTrace(); // can't come here } }; Thread aThread = new Thread(r); aThread.start(); Runnable r2 = () -> { try { Thread.currentThread().sleep(250); synchronized (o) { System.out.println("bThread get lock"); while (true) { } } } catch (InterruptedException e) { e.printStackTrace(); } }; Thread bThread = new Thread(r2); bThread.start(); try { Thread.currentThread().sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } // interrupt aThread aThread.interrupt(); }
其他阻塞
在 API 中还有其他的阻塞情况。如果线程被
InterruptibleChannel
的IO操作阻塞,则这个管道会被关闭,线程的中断状态会被置位,同时会抛出ClosedByInterruptException
异常。如果线程被
Selector
阻塞,则这个线程的中断状态会被置位,并且立即从选择操作中返回,可能返回一个非零值,就像selector
的wakeup
方法被调用一样。这两种阻塞我都没有用过(太菜了),所以没有写代码去测试,之后遇到我会去测试。
除了以上这几种情况,其他情况都会置位线程的中断状态。
参考
- Java核心技术 卷1 12章
- Java8 API