interrupted()和isInterrupted()

简介: interrupted()和isInterrupted()

前言

当提及如何终止一个线程时,部分读者通常立马想到的方法肯定是stop(),但是stop()方法并不被推荐使用(很多规范中是禁止使用的),其原因是强制终止一个线程,会导致程序不正常的结束,会出现资源未正确释放、程序结果不正确等等问题。而是否终止一个线程应该把这个控制权转交给当前被终止的线程本身,此时采用的办法就是interrupt()方法来终止,该方法相当于修改一个共享变量的值,当运行中的线程判断当前值为false则继续运行,如果有地方调用当前thread的interrupt()方法,那么这个值将变为true,此时当前线程可以根据这个值的修改来正确的终止线程的运行。


API

在java.lang.Thread中主要提供了如下与线程中断相关的方法,其具体方法名与主要作用如下表所示。

方法名

方法作用

public void interrupt()

中断此线程

public static boolean interrupted()

测试当前线程是否被中断,该方法会恢复(清除)中断标志

public boolean isInterrupted()

测试当前线程是否被中断,该方法只会获取中断标志,不会恢复(清除)中断标志

private native boolean isInterrupted(boolean ClearInterrupted);

interrupted()和isInterrupted()最终调用,该方法是native本地方法,在jvm中具体实现,也是获取线程中断标志真正调用的方法,参数ClearInterrupted意思是是否恢复(清除)中断标志

源码

/**

* 中断此线程

*/

public void interrupt() {

   if (this != Thread.currentThread())

       checkAccess();


   synchronized (blockerLock) {

       Interruptible b = blocker;

       if (b != null) {

           interrupt0();           // Just to set the interrupt flag

           b.interrupt(this);

           return;

       }

   }

   interrupt0();

}


/**

* 测试当前线程是否被中断,返回中断标志

*/

public static boolean interrupted() {

   return currentThread().isInterrupted(true);

}


/**

* 测试当前线程是否被中断,返回中断标志

*/

public boolean isInterrupted() {

   return isInterrupted(false);

}


/**

* 线程是否被中断native方法,ClearInterrupted为是否清除中断标志参数

*/

private native boolean isInterrupted(boolean ClearInterrupted);


/**

* 中断当前线程的native方法

*/

private native void interrupt0();

interrupted()和isInterrupted()区别

看了上述API讲述和Thread中的源码,已经清楚interrupted()和isInterrupted()的主要区别了

  1. interrupted()为静态方法,isInterrupted()为普通方法
  2. interrupted() 返回中断标志且清除(恢复)中断标志,isInterrupted()仅返回中断标志

使用方法


我们先验证中断异常响应,通过如下两种方法的使用示例来介绍,注意Runner中的run方法的部分区别

方法一

package com.liziba.p7;


import java.util.concurrent.TimeUnit;


/**

* <p>

*      

* </p>

*

* @Author: Liziba

* @Date: 2021/6/24 21:05

*/

public class ThreadInterruptedDemo {

   

   public static void main(String[] args) throws InterruptedException {

       Thread t1 = new Thread(new Runner(), "Thread-01");

       t1.start();

       // 主线程睡眠1秒,保证t1的充分执行

       TimeUnit.SECONDS.sleep(1);

       // 发起中断

       t1.interrupt();

   }


   static class Runner implements Runnable {


       @Override

       public void run() {

           while (!Thread.currentThread().isInterrupted()) {

               System.out.println(Thread.currentThread().getName() + " is running .");

           }

       }

   }


}

输出结果

可以看到线程在执行数次后终止运行

方法二

package com.liziba.p7;


import java.util.concurrent.TimeUnit;


/**

* <p>

*

* </p>

*

* @Author: Liziba

* @Date: 2021/6/24 21:18

*/

public class ThreadInterruptedDemo {


   public static void main(String[] args) throws InterruptedException {

       Thread t1 = new Thread(new Runner(), "Thread-01");

       t1.start();

       // 主线程睡眠2秒,保证t1的充分执行

       TimeUnit.SECONDS.sleep(1);

       // 发起中断

       t1.interrupt();

   }


   static class Runner implements Runnable {


       @Override

       public void run() {

           while (!Thread.currentThread().isInterrupted()) {

               System.out.println(Thread.currentThread().getName() + " is running .");

               try {

                   // 睡眠2秒,保证主线程发起的中断能被捕获

                   TimeUnit.SECONDS.sleep(2);

               } catch (InterruptedException e) {

                   // 不对中断做任何处理,try住异常,打印

                   e.printStackTrace();

               }

           }

       }

   }


}

输出结果

可以看到main线程中发起的t1线程中断,被捕获住异常后,未做任何处理,线程继续持续不断的运行

总结上述两种方式

方法一和方法二,均通过判断Thread.currentThread().isInterrupted()的值来运行run方法中的逻辑,Thread.currentThread().isInterrupted()在线程未中断时返回false,当main线程中执行 t1.interrupt()时,线程t1被中断,Thread.currentThread().isInterrupted()的值变为false;在方法一中,获取到这个变化后直接结束运行;在方法二中,由于sleep()使得线程阻塞会响应中断,但是此时我仅仅catch住异常,并没有对中断做任何处理,这里有个知识点是,线程响应中断抛出异常时,会恢复(清除)中断标志,所以t1.interrupt()对中断标志的修改又被恢复了,程序仍然不断的运行。


接下来我们来验证interrupted()对于中断的标志的清除

package com.liziba.p7;


import java.util.concurrent.TimeUnit;


/**

* <p>

*  isInterrupted()

* </p>

*

* @Author: Liziba

* @Date: 2021/6/24 21:20

*/

public class ThreadInterruptDemo2 {


   public static void main(String[] args) throws InterruptedException {


       Thread thread = new Thread(new Runner(), "Thread-1");

       thread.start();

       TimeUnit.SECONDS.sleep(2);

       thread.interrupt();

   }



   static class Runner implements Runnable {


       @Override

       public void run() {

           System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.currentThread().isInterrupted());


           while (!Thread.currentThread().isInterrupted()) {

               try {

                   System.out.println(Thread.currentThread().getName() + " is running .");

                   TimeUnit.SECONDS.sleep(1);

               } catch (InterruptedException e) {

                   // 响应中断,抛出异常后中断位置会被复位,自己中断自己

                   Thread.currentThread().interrupt();

                   // 这里调用isInterrupted()获取当前的中断标志

                   System.out.println(Thread.currentThread().getName()

                           +" interrupted flag is " + Thread.currentThread().isInterrupted());

               }

           }

       }

   }


}

输出结果

这里证明interrupted()不清楚中断标志,线程在获取到 thread.interrupt()发起中断后,执行结束。


将上述catch中的Thread.currentThread().isInterrupted()修改为Thread.interrupted()再次运行

package com.liziba.p7;


import java.util.concurrent.TimeUnit;


/**

* <p>

*

* </p>

*

* @Author: Liziba

* @Date: 2021/6/24 21:23

*/

public class ThreadInterruptDemo2 {



   public static void main(String[] args) throws InterruptedException {


       Thread thread = new Thread(new Runner(), "Thread-1");

       thread.start();

       TimeUnit.SECONDS.sleep(2);

       thread.interrupt();

   }


   // 区别在catch中

   static class Runner implements Runnable {


       @Override

       public void run() {

           System.out.println(Thread.currentThread().getName() +" interrupted flag is " + Thread.currentThread().isInterrupted());


           while (!Thread.currentThread().isInterrupted()) {

               try {

                   System.out.println(Thread.currentThread().getName() + " is running .");

                   TimeUnit.SECONDS.sleep(1);

               } catch (InterruptedException e) {

                   // 响应中断,抛出异常后中断位置会被复位,自己中断自己

                   Thread.currentThread().interrupt();

                   // 注意区别在这里

                   System.out.println(Thread.currentThread().getName()

                           +" interrupted flag is " + Thread.interrupted());

               }

           }

       }

   }


}

输出结果

线程也响应到了 thread.interrupt()的中断,但是由于catch中调用了Thread.interrupted(),对中断标志进行了清除,所以!Thread.currentThread().isInterrupted()判断仍然等于true,线程继续不断的运行


看到这里,应该已经理解了这两个方法的主要区别和其使用,最后我们来看下一个源码中的使用案例。我们通过观看AbstractQueuedSynchronizer(AQS)中的await()方法,来看其在源码中的使用。

public final void await() throws InterruptedException {

   // 判断当前线程是否被中断,如果被中断则恢复中断标志

   if (Thread.interrupted())

       throw new InterruptedException();

   Node node = addConditionWaiter();

   int savedState = fullyRelease(node);

   int interruptMode = 0;

   while (!isOnSyncQueue(node)) {

       LockSupport.park(this);

       if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)

           break;

   }

   if (acquireQueued(node, savedState) && interruptMode != THROW_IE)

       interruptMode = REINTERRUPT;

   if (node.nextWaiter != null) // clean up if cancelled

       unlinkCancelledWaiters();

   if (interruptMode != 0)

       reportInterruptAfterWait(interruptMode);

}

AbstractQueuedSynchronizer(AQS)源码中使用静态Thread.interrupted(),判断当前线程是否被中断,并恢复中断标志,如果线程已被中断则抛出InterruptedException中断异常。清除标志位的作用就是为了当前线程响应过中断后,再次进入的时候可以进行后续操作。

目录
相关文章
|
负载均衡 应用服务中间件 API
Nginx、Kong、Apisix、Gateway网关比较
Nginx、Kong、Apisix、Gateway网关比较
4791 1
Nginx、Kong、Apisix、Gateway网关比较
|
SQL 关系型数据库 数据库
学习分布式事务Seata看这一篇就够了,建议收藏
学习分布式事务Seata看这一篇就够了,建议收藏
20080 2
|
Java 数据库连接 Maven
springBoot:项目建立&配置修改&yaml的使用&resource 文件夹(二)
本文档介绍了如何创建一个基于Maven的项目,并配置阿里云仓库、数据库连接、端口号、自定义启动横幅及多环境配置等。同时,详细说明了如何使用YAML格式进行配置,以及如何处理静态资源和模板文件。文档还涵盖了Spring Boot项目的`application.properties`和`application.yaml`文件的配置方法,包括设置数据库驱动、URL、用户名、密码等关键信息,以及如何通过配置文件管理不同环境下的应用设置。
993 1
|
9月前
|
消息中间件 架构师 Apache
一本书精通Apache RocketMQ
一本书精通Apache RocketMQ
341 3
|
存储 SQL 分布式计算
Hadoop生态系统概述:构建大数据处理与分析的基石
【8月更文挑战第25天】Hadoop生态系统为大数据处理和分析提供了强大的基础设施和工具集。通过不断扩展和优化其组件和功能,Hadoop将继续在大数据时代发挥重要作用。
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
488 3
|
SQL NoSQL 数据库
SpringCloud基础6——分布式事务,Seata
分布式事务、ACID原则、CAP定理、Seata、Seata的四种分布式方案:XA、AT、TCC、SAGA模式
SpringCloud基础6——分布式事务,Seata
|
供应链
不懂运营,那你没必要做知识付费了
在一次知识付费行业的线下交流会上,三位来自不同领域的嘉宾分享了他们的见解。一位金融教育培训行业的专家强调了在竞争激烈的市场中深耕精细化运营的重要性,并提出了围绕目标群体需求制作高质量、有针对性的知识产品的策略。此外,他还建议避免自行研发教育软件,转而采用第三方专业服务商提供的系统。第二位嘉宾来自一家时尚教育机构,他们通过独特的UGC社群玩法,组织用户讨论并从中提炼内容,最终形成知识产品。这种模式不仅激励了社群成员积极参与,还确保了高质量的内容产出。
97 4
|
存储 人工智能 安全
大环境下AI发展迅速,如何保证AI的安全问题?
保障AI安全的关键措施包括:数据隐私保护(加密、访问控制、脱敏、共享协议)、模型安全(验证、鲁棒性、监测、更新)、用户信息保护(透明收集、匿名化、保密协议)、网络安全(实时监测、防护措施)和合规伦理(遵守法规、融入设计)。此外,安全培训和意识提升也是重要一环。多角度策略确保AI技术的安全、健康和可持续发展。
2545 0
|
缓存 监控 NoSQL