我们都知道,Java中停止一个线程不能用stop,因为stop会瞬间强行停止一个线程,且该线程持有的锁并不能释放。大家多习惯于用interrupt,那么使用它又有什么需要注意的呢?

interrupt相关的方法

Java中和interrupt相关的方法有三个

  1. public boolean isInterrupted()
  2. public void interrupt()
  3. public static boolean interrupted()

boolean isInterrupted()

每个线程都一个状态位用于标识当前线程对象是否是中断状态。isInterrupted主要用于判断当前线程对象的中断标志位是否被标记了,如果被标记了则返回true,表示当前已经被中断,否则返回false。我们也可以看看它的实现源码:

  1. public boolean isInterrupted() {
  2. return isInterrupted(false);
  3. }
  4. private native boolean isInterrupted(boolean ClearInterrupted);

底层调用的native方法isInterrupted,传入一个boolean类型的参数,用于指定调用该方法之后是否需要清除该线程的中断标识位。从这里我们也可以看出来,调用isInterrupted()并不会清除线程的中断标识位。

void interrupt()

interrupt()用于设置当前线程对象的中断标识位,其源码为:

  1. public void interrupt() {
  2. // 检查当前线程是否有权限修改目标线程,如果没有,则会抛出异常SecurityException
  3. if (this != Thread.currentThread())
  4. checkAccess();
  5. synchronized (blockerLock) {
  6. Interruptible b = blocker;
  7. if (b != null) {
  8. interrupt0(); // Just to set the interrupt flag
  9. b.interrupt(this);
  10. return;
  11. }
  12. }
  13. interrupt0();
  14. }

blockerLockblocker都和阻塞IO时产生的中断相关,因此推测interrupt()需要当阻塞IO操作执行完之后,才可以执行。

interrupt()其实只是改变了一个标志位,对于线程本身的状态并没有影响。

boolean interrupted()

该方法是一个静态的方法,用于返回当前线程是否被中断,其源码是:

  1. public static boolean interrupted() {
  2. return currentThread().isInterrupted(true);
  3. }

需要注意的是:该方法调用结束的时候会清空中断标识位

线程的状态与中断的关系

我们知道,Java中的线程一共6种状态,分别是NEWRUNNABLEBLOCKEDWAITINGTIMED_WAITINGTERMINATED(Thread类中有一个State枚举类型列举了线程的所有状态)。下面我们就将把线程分别置于上述的不同种状态,然后看看中断操作对它们的影响。

NEW和TERMINATED

NEW状态表示线程还未调用start()方法,TERMINATED状态表示线程已经运行终止。

这两个状态下调用中断方法来中断线程的时候,Java认为毫无意义,所以并不会设置线程的中断标识位。例如:

NEW状态:

  1. public static void main(String[] args) {
  2. Thread thread = new Thread();
  3. System.out.println(thread.getState());
  4. System.out.println(thread.isInterrupted());
  5. thread.interrupt();
  6. System.out.println(thread.isInterrupted());
  7. }

输出结果:

  1. NEW
  2. false
  3. false

TERMINATED状态:

  1. public static void main(String[] args) throws InterruptedException {
  2. Thread thread = new Thread();
  3. // 开始线程
  4. thread.start();
  5. // 等待线程结束
  6. thread.join();
  7. System.out.println(thread.getState());
  8. System.out.println(thread.isInterrupted());
  9. thread.interrupt();
  10. System.out.println(thread.isInterrupted());
  11. }

输出结果:

  1. TERMINATED
  2. false
  3. false

从上述的两个例子来看,处于NEWTERMINATED状态的线程,对于中断是屏蔽的,也就是说中断操作对这两种状态下的线程是无效的。

RUNNABLE

处于RUNNABLE状态的线程,当中断线程后,会修改其中断标志位,但并不会影响线程本身。例如:

  1. /**
  2. * 自定义线程类
  3. */
  4. public class MyThread extends Thread{
  5. @Override
  6. public void run(){
  7. while(true){
  8. // 什么都不做,就是空转
  9. }
  10. }
  11. public static void main(String[] args) {
  12. Thread thread = new MyThread();
  13. thread.start();
  14. System.out.println(thread.getState());
  15. System.out.println(thread.isInterrupted());
  16. thread.interrupt();
  17. System.out.println(thread.isInterrupted());
  18. System.out.println(thread.getState());
  19. }
  20. }

结果为:

  1. RUNNABLE
  2. false
  3. true
  4. RUNNABLE

中断标志位确实被改变了,但线程依旧继续运行。那我们调用interrupt()方法的意义在哪儿?

其实Java是将中断线程的权利交给了我们自己的程序,通过中断标志位,我们的程序可以通过boolean isInterrupted()方法来判断当前线程是否中断,从而决定之后的操作。

我们可以在此基础上,保证执行任务的原子性。例如修改MyThread类的方法:

  1. /**
  2. * 自定义线程类
  3. */
  4. public class MyThread extends Thread{
  5. @Override
  6. public void run(){
  7. while(true){
  8. if (this.isInterrupted()){
  9. System.out.println("exit MyThread");
  10. break;
  11. }
  12. }
  13. }
  14. public static void main(String[] args) throws InterruptedException {
  15. Thread thread = new MyThread();
  16. thread.start();
  17. System.out.println(thread.getState());
  18. System.out.println(thread.isInterrupted());
  19. thread.interrupt();
  20. System.out.println(thread.isInterrupted());
  21. thread.join();
  22. System.out.println(thread.getState());
  23. }
  24. }

结果为:

  1. RUNNABLE
  2. false
  3. true
  4. exit MyThread
  5. TERMINATED

BLOCKED

当线程处于BLOCKED状态,说明该线程由于竞争某个对象的锁失败而被挂在了该对象的阻塞队列上了。

那么此时发起中断操作不会对该线程产生任何影响,依然只是设置中断标志位。例如:

  1. /**
  2. * 自定义线程类
  3. */
  4. public class MyThread extends Thread{
  5. public synchronized static void doSomething(){
  6. while(true){
  7. // 空转
  8. }
  9. }
  10. @Override
  11. public void run(){
  12. doSomething();
  13. }
  14. public static void main(String[] args) throws InterruptedException {
  15. // 启动两个线程
  16. Thread thread1 = new MyThread();
  17. thread1.start();
  18. Thread thread2 = new MyThread();
  19. thread2.start();
  20. Thread.sleep(1000);
  21. System.out.println(thread1.getState());
  22. System.out.println(thread2.getState());
  23. System.out.println(thread2.isInterrupted());
  24. thread2.interrupt();
  25. System.out.println(thread2.isInterrupted());
  26. System.out.println(thread2.getState());
  27. }
  28. }

结果为:

  1. RUNNABLE
  2. BLOCKED
  3. false
  4. true
  5. BLOCKED

thread2处于BLOCKED状态,执行中断操作之后,该线程仍然处于BLOCKED状态,但是中断标志位却已被修改。

这种状态下的线程和处于RUNNABLE状态下的线程是类似的,给了我们程序更大的灵活性去判断和处理中断。

WAITING/TIMED_WAITING

这两种状态本质上是同一种状态,只不过TIMED_WAITING在等待一段时间后会自动释放自己,而WAITING则是无限期等待,需要其他线程调用类似notify方法释放自己。但是他们都是线程在运行的过程中由于缺少某些条件而被挂起在某个对象的等待队列上。

当这些线程遇到中断操作的时候,会抛出一个InterruptedException异常,并清空中断标志位。例如:

  1. /**
  2. * 自定义线程类
  3. */
  4. public class MyThread extends Thread{
  5. @Override
  6. public void run(){
  7. synchronized (this){
  8. try {
  9. wait();
  10. } catch (InterruptedException e) {
  11. System.out.println("catch InterruptedException");
  12. }
  13. }
  14. }
  15. public static void main(String[] args) throws InterruptedException {
  16. Thread thread = new MyThread();
  17. thread.start();
  18. Thread.sleep(1000);
  19. System.out.println(thread.getState());
  20. System.out.println(thread.isInterrupted());
  21. thread.interrupt();
  22. Thread.sleep(1000);
  23. System.out.println(thread.isInterrupted());
  24. }
  25. }

结果为:

  1. WAITING
  2. false
  3. catch InterruptedException
  4. false

从运行结果看,当线程启动之后就被挂起到该线程对象的等待队列上,然后我们调用interrupt()方法对该线程进行中断,输出了我们在catch中的输出语句,显然是捕获了InterruptedException异常,接着就看到该线程的中断标志位被清空。

因此我们要么就在catch语句中结束线程,否则就在catch语句中加上this.interrupt();,再次设置标志位,这样也方便在之后的逻辑或者其他地方继续判断。

总结

我们介绍了线程在不同状态下对于中断请求的反应:

  1. NEWTERMINATED对于中断操作几乎是屏蔽的。
  2. RUNNABLEBLOCKED类似,对于中断操作只是设置中断标志位并没有强制终止线程,对于线程的终止权利依然在程序手中。
  3. WAITINGTIMED_WAITING状态下的线程对于中断操作是敏感的,他们会抛出异常并清空中断标志位。

有兴趣的话可以关注我的公众号或者头条号,说不定会有意外的惊喜。

Java面试-interrupt的更多相关文章

  1. Java面试官最常问的volatile关键字

    在Java相关的职位面试中,很多Java面试官都喜欢考察应聘者对Java并发的了解程度,以volatile关键字为切入点,往往会问到底,Java内存模型(JMM)和Java并发编程的一些特点都会被牵扯 ...

  2. Java面试 32个核心必考点完全解析

    目录 课程预习 1.1 课程内容分为三个模块 1.2 换工作面临问题 1.3 课程特色 课时1:技术人职业发展路径 1.1 工程师发展路径 1.2 常见技术岗位划分 1.3 面试岗位选择 1.4 常见 ...

  3. Java面试知识点之线程篇(三)

    前言:这里继续对java线程相关知识点进行总结,不能间断. 1.yield()方法 yield()的作用是让步.它能让当前线程由“运行状态”进入到“就绪状态”,从而让其它具有相同优先级的等待线程获取执 ...

  4. 最近5年183个Java面试问题列表及答案[最全]

    Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 Stri ...

  5. 转 最近5年183个Java面试问题列表及答案[最全]

    Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别(String 类型和 StringBuffer 类型的主要性能区别其实在于 Stri ...

  6. 慕课网:剑指Java面试-Offer直通车视频课程

    慕课网:剑指Java面试-Offer直通车视频课程,一共有10个章节. 目录结构如下: 目录:/2020036-慕课网:剑指Java面试-Offer直通车 [6G] ┣━━第10章 Java常用类库与 ...

  7. Java面试宝典2017

    JAVA面试.笔试题(2017版)                 欲想成功,必须用功!   目录 一.                  HTML&CSS部分................ ...

  8. JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结

    我是一名java开发人员,hibernate以及mybatis都有过学习,在java面试中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解和 ...

  9. 转:最近5年133个Java面试问题列表

    最近5年133个Java面试问题列表 Java 面试随着时间的改变而改变.在过去的日子里,当你知道 String 和 StringBuilder 的区别就能让你直接进入第二轮面试,但是现在问题变得越来 ...

随机推荐

  1. 随笔编号-16 JAVA知识框架

    基于 J2EE 列举的知识架构,大体列举开发基础知识.帮助我随时查缺补漏,奉行好记性不如烂笔头.写了这该随笔,以便后续查询. 1  JAVA简介 2  JAVA编程环境 3  JAVA基本编程结构 4 ...

  2. STM32F0系列芯片SPI发送一字节数据却输出16个CLK时钟的解决办法

    问题 上一个项目在用寄存器操作STM32F0芯片的SPI_DR寄存器的时候,发现一个问题: 我给DR寄存器赋值一个uint8_t一字节大小的数据,SPI引脚能正确输出数据和时钟,但前面八位正确的数据输 ...

  3. 利用ShardingSphere-JDBC实现分库分表--配置中心的实现

    在之前的文章中我详细描述了如何利用ShardingSphere-JDBC进行分库分表,同时也实现了简单的精确分库算法接口,详情见下面的链接: 利用ShardingSphere-JDBC实现分库分表 但 ...

  4. 设计模式(C#)——06桥接模式

    推荐阅读:  我的CSDN  我的博客园  QQ群:704621321       在早先,几乎每个手机的充电器接口都是不同的.每个型号的手机都有一个充电器,此时我们把充电器作为一个抽象类,抽象类中提 ...

  5. HDU 6134

    题意略. 思路: 我们先不考虑[(i , j) == 1],在此情况下,其实这个值是sum( [ (i , j) == 1,2,3,....,n ] ) 这些情况.我们要求的仅仅是其中的第一部分而已. ...

  6. 漫话:如何给女朋友解释什么是"锟斤拷"?

    漫话:如何给女朋友解释什么是"锟斤拷"? ​ 周末女朋友出去逛街了,我自己一个人在家看综艺节目,突然,女朋友给我打来电话. 过了一会,女朋友回来了,她拿出手机,给我看了她在超市拍的 ...

  7. Interger等包装类的比较

    Integer a = 1; integer b = 1; integer c = 500; integer d=500; System.out.print(a==b); System.out.pri ...

  8. Java连载25-方法讲解

    一.方法 1.方法的基础语法 (1)例子 //需求1:请编写程序计算10和20的和,并将结果输出 int a = 10; int b = 20; System.out.print(a + " ...

  9. 关于AndroidStudio在真机安装的apk闪退(无法打开)的解决方案

    问题描述: 重新安装AndroidStudio之后 1.发现在真机上安装apk时显示的是应用包名. 2.在真机上安装的apk无法打开,一直闪退. 如图: 解决方案: 关闭AndroidStudio的I ...

  10. (五十八)c#Winform自定义控件-管道阀门(工业)

    前提 入行已经7,8年了,一直想做一套漂亮点的自定义控件,于是就有了本系列文章. GitHub:https://github.com/kwwwvagaa/NetWinformControl 码云:ht ...