JUC 六. 线程中断 与 LockSupport

2023-11-10

一. 基础理解

  1. 先了解几个问题:
  1. 怎么中断一个运行中的线程
  2. 多线程中断标识是什么
  3. 中断后线程是不是就立刻停止运行了
  1. 注意: 一个线程不应该由其他线程来强制中断或停止,而是应该有线程自己自行停止,所以Thread.stop, Thread.suspend, Thread.resume都已经被废弃
  2. 每个线程中都有一个用于表示线程是否被中断的标识,当标识为true时说明被中断,false是表示未被中断,通过调用interrupt()方法设置,可以在别的线程中调用,也可以在别的线程中调用,
  3. 注意点这是java提供的一种中断线程的中断机制,interrupt()该方法也仅仅将线程对象设置为中断状态,实际线程并不能立即停止,在后续操作中需要不断的检测线程是否被中断
  4. 几个中断相关的api
    在这里插入图片描述
    在这里插入图片描述

如何退出一个线程

  1. 面试题: 如何优雅的退出或停止一个线程: 需要中断的线程不停监听中断标识状态,一旦判断为中断状态,则跳出逻辑,有一下几种方式
  1. volatile 方式
  2. AtomicBoolean
  3. Thread中自带的中断api

volatile 与 AtomicBoolean 中断线程示例

  1. volatile 示例
  1. 声明一个中断线程标识interruptFlags true为中断,使用 volatile 修饰
  2. 在 voiatileInterruptTest() 方法中开启了两个线程a与b,
  3. a线程执行时判断中断标识,如果为true说明要中断了,跳出业务
  4. b线程中修改中断标识,例如根据某种业务逻辑,判断a线程需要停止运行了,就设置标识为true,a线程判断状态为true,就跳出
import java.util.concurrent.TimeUnit;
public class InterruptDemo {

    //是否中断某个线程的标识,使用volatile修饰
    private volatile boolean interruptFlags = false;

    public void voiatileInterruptTest() throws InterruptedException {
        new Thread(() -> {
            while (true) {
                if (interruptFlags) {
                    System.out.println(Thread.currentThread().getName() + " 线程中断");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " 线程正常执行");
            }
        }, "a").start();

        TimeUnit.SECONDS.sleep(5L);

        new Thread(() -> {
            interruptFlags = true;
            System.out.println(Thread.currentThread().getName() + " 将线程a设置为中断状态");
        }, "b").start();
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptDemo demo = new InterruptDemo();
        demo.voiatileInterrupt();
    }
}
  1. AtomicBoolean 示例
  1. 将中断线程标识设置为AtomicBoolean 原子类型变量
  2. a线程执行时先判断原子类型变量
  3. b线程中根据业务逻辑修改a线程的中断标识
	//是否中断线程标识
	private AtomicBoolean isStop = new AtomicBoolean(false);

    public void atomiInterrupt() throws InterruptedException {
        new Thread(() -> {
            while (true) {
                if (isStop.get()) {
                    System.out.println(Thread.currentThread().getName() + " 线程中断");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " 线程正常执行");
            }
        }, "a").start();

        TimeUnit.SECONDS.sleep(5L);

        new Thread(() -> {
            isStop.set(true);
            System.out.println(Thread.currentThread().getName() + " 将线程a设置为中断状态");
        }, "b").start();
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptDemo demo = new InterruptDemo();
        demo.atomiInterrupt();
    }
  1. 总结: 不管是volatile 方式还是 Atomic原子类方式,1. 其底层关注点都是变量线程可见性的问题,一个线程中修改变量,另外一个线程中能及时拿到修改后的值, 2. 要中断的线程中要判断这个代表中断的标识,拿到状态,需要停止则跳出

Thread中自带的中断api示例

  1. 示例代码
  1. 主要通过 interrupt()设置线程中断标志位位中断状态true, isInterrupted()获取线程的中断标志位,如果是true并清除中断标识ture修改为false
  2. t1线程中执行业务逻辑时先isInterrupted()获取当前线程的中断标志位,如果为中断状态则跳出
  3. t2线程中根据业务逻辑,拿t1线程设置t1它自己为中断状态,
	public void threadApiInterrupt() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                //1.t1线程中判断自己线程的中断标志位,当为中断状态true时跳出执行
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + " 线程中断");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " 线程正常执行");
            }
        }, "t1");

        t1.start();

        TimeUnit.SECONDS.sleep(5L);

        Thread t2 = new Thread(() -> {
            //1.通过 t1 线程自己设置自己线程中断标志为true,中断状态
            t1.interrupt();
            System.out.println(Thread.currentThread().getName() + " 将线程a设置为中断状态");
        }, "t2");

        t2.start();
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptDemo demo = new InterruptDemo();
        demo.threadApiInterrupt();
    }
  1. 有个主意点: 在设置线程中断后,被设置中断的线程不会停止,只是将线程中断标志设置为true,没有其它任何操作,想让被设置的线程中断停止,需要程序员手动去实现,例如获取中断标志,手动break
  2. 并且: 假设线程处于被阻塞状态例如(sleep, wait, join等),此时如果对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并且抛出InterruptedException异常

阻塞状态线程中断时异常解决

  1. 上面说到了线程处于被阻塞状态例如(sleep, wait, join等),此时如果对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并且抛出InterruptedException异常, 在业务中也就是说当前这个线程是阻塞状态,然后一调用中断,由阻塞退出了,interrupt中断状态true变为false,并抛出了异常,这样就会造成: 1 原阻塞线程不再阻塞, 2. 中断标识由true变为了false
  2. 解决以上问题(在第3步中)
public void threadApiInterruptSleep() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                //1.t1线程中判断自己线程的中断标志位,当为中断状态true时跳出执行
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println(Thread.currentThread().getName() + " 线程中断");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + " 线程正常执行");

                //2.t1线程中调用sleep阻塞
                try {
                    TimeUnit.SECONDS.sleep(5L);
                } catch (InterruptedException e) {
                    //3.防止阻塞中的线程调用interrupt()设置中断报异常,中断无效
                    //在catch中当前需要中断的线程再发起一次中断,调用interrupt()
                    Thread.currentThread().interrupt();
                    System.out.println(Thread.currentThread().getName()+"线程再次中断 ");

                    e.printStackTrace();
                }
            }
        }, "t1");

        t1.start();

        TimeUnit.SECONDS.sleep(2L);

        Thread t2 = new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + " 将线程a设置为中断状态");
            //2.通过 t1 线程自己设置自己线程中断标志为true,中断状态
            t1.interrupt();
            t1.isInterrupted();
        }, "t2");

        t2.start();
    }

    public static void main(String[] args) throws InterruptedException {
        InterruptDemo demo = new InterruptDemo();
        demo.threadApiInterruptSleep();
    }

二. Thread中自带的中断底层分析

  1. 查看 interrupt() 设置线程为中断状态接口源码,发现调用到底层是native的interrupt0()方法
	//1. 
	public void interrupt() {
        if (this != Thread.currentThread())
            checkAccess();
        synchronized (blockerLock) {
            Interruptible b = blocker;
            if (b != null) {
            	//实际调用了interrupt0()方法
                interrupt0();           // Just to set the interrupt flag
                b.interrupt(this);
                return;
            }
        }
        //实际调用了interrupt0()方法
        interrupt0();
    }

	//2.查看interrupt0()发现该方法是native
	private native void interrupt0();
  1. 查看 isInterrupted() 源码,其底层也是调用native的isInterrupted(boolean ClearInterrupted),并且会传递一个false,将中断标识设置为false
	public boolean isInterrupted() {
        return isInterrupted(false);
    }
	private native boolean isInterrupted(boolean ClearInterrupted);

三. 总结

  1. 以前设置中断线程的方法: volatile 或 AtomicBoolean 方式,都是基于变量可见性的特性: 例如 a线程执行时判断中断标识,如果为true说明要中断了,跳出业务, b线程中修改中断标识,例如根据某种业务逻辑,判断a线程需要停止运行了,就设置标识为true,a线程判断状态为true,就跳出
  2. 讲一下规范中有个要求: 一个线程不应该由其他线程来强制中断或停止,而是应该有线程自己自行停止,所以Thread.stop, Thread.suspend, Thread.resume都已经被废弃
  3. Thread中自带的中断api: 通过 interrupt()设置线程中断标志位位中断状态true, isInterrupted()获取线程的中断标志位,如果是true并清除中断标识ture修改为false, 例如
  1. t1线程中执行业务逻辑时先isInterrupted()获取当前线程的中断标志位,如果为中断状态则跳出
  2. t2线程中根据业务逻辑,拿t1线程设置t1它自己为中断状态,
  1. 使用interrupt()方法中断线程的注意点:
  1. 线程并不会自动中断,需要调用isInterrupted()获取状态,手动去break
  2. 如果线程处于被阻塞状态例如(sleep, wait, join等),对该线程调用interrupt()方法,会造成线程立即退出阻塞状态,并抛出InterruptedException异常
  1. 如何解决中断阻塞状态线程抛出异常问题: tryCatch捕获InterruptedException异常,当发生该异常时在catch中再次调用interrupt()设置中断标识
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

JUC 六. 线程中断 与 LockSupport 的相关文章

  • 如何让 BlazeDS 忽略属性?

    我有一个 java 类 它有一个带有 getter 和 setter 的字段 以及第二对 getter 和 setter 它们以另一种方式访问 该字段 public class NullAbleId private static final
  • 在 Java 中克隆对象 [3 个问题]

    这样做会调用Asub的clone方法吗 或者Asub深度克隆是否正确 如果没有的话 有没有办法通过这种方法对Asub进行深度克隆呢 abstract class Top extends TopMost protected Object cl
  • 如何获取之前的URL?

    我需要调用我的网络应用程序的 URL 例如 如果有一个从 stackoverflow com 到我的网站 foo com 的链接 我需要 Web 应用程序 托管 bean 中的 stackoverflow 链接 感谢所有帮助 谢谢 并不总是
  • 将流转换为 IntStream

    我有一种感觉 我在这里错过了一些东西 我发现自己做了以下事情 private static int getHighestValue Map
  • 在 junit 测试中获取 javax.lang.model.element.Element 类

    我想测试我的实用程序类 ElementUtils 但我不知道如何将类作为元素获取 在 AnnotationProcessors 中 我使用以下代码获取元素 Set
  • jdbc mysql loginTimeout 不起作用

    有人可以解释一下为什么下面的程序在 3 秒后超时 因为我将其设置为在 3 秒后超时 12秒 我特意关闭了mysql服务器来测试mysql服务器无法访问的这种场景 import java sql Connection import java
  • Hibernate 的 PersistentSet 不使用 hashCode/equals 的自定义实现

    所以我有一本实体书 public class Book private String id private String name private String description private Image coverImage pr
  • 如何在用户输入数据后重新运行java代码

    嘿 我有一个基本的java 应用程序 显示人们是成年人还是青少年等 我从java开始 在用户输入年龄和字符串后我找不到如何制作它它们被归类为 我希望它重新运行整个过程 以便其他人可以尝试 的节目 我一直在考虑做一个循环 但这对我来说没有用
  • tomcat 中受密码保护的应用程序

    我正在使用 JSP Servlet 开发一个Web应用程序 并且我使用了Tomcat 7 0 33 as a web container 所以我的要求是tomcat中的每个应用程序都会password像受保护的manager applica
  • 如何对不同的参数类型使用相同的java方法?

    我的问题 我有 2 个已定义的记录 创建对象请求 更新对象请求 必须通过实用方法进行验证 由于这两个对象具有相同的字段 因此可以对这两种类型应用相同的验证方法 现在我只是使用两种方法进行重载 但它很冗长 public record Crea
  • 为什么 Java 8 不允许非公共默认方法?

    让我们举个例子 public interface Testerface default public String example return Hello public class Tester implements Testerface
  • 不接受任何内容也不返回任何内容的函数接口[重复]

    这个问题在这里已经有答案了 JDK中是否有一个标准的函数式接口 不接受也不返回任何内容 我找不到一个 像下面这样 FunctionalInterface interface Action void execute 可运行怎么样 Functi
  • Cucumber 0.4.3 (cuke4duke) 与 java + maven gem 问题

    我最近开始为 Cucumber 安装一个示例项目 并尝试使用 maven java 运行它 我遵循了这个指南 http www goodercode com wp using cucumber tests with maven and ja
  • Opencv Java 灰度

    我编写了以下程序 尝试从彩色转换为灰度 Mat newImage Imgcodecs imread q1 jpg Mat image new Mat new Size newImage cols newImage rows CvType C
  • 使用反射覆盖最终静态字段是否有限制?

    在我的一些单元测试中 我在最终静态字段上的反射中遇到了奇怪的行为 下面是说明我的问题的示例 我有一个基本的 Singleton 类 其中包含一个 Integer public class BasicHolder private static
  • 如何将双精度/浮点四舍五入为二进制精度?

    我正在编写对浮点数执行计算的代码的测试 不出所料 结果很少是准确的 我想在计算结果和预期结果之间设置一个容差 我已经证实 在实践中 使用双精度 在对最后两位有效小数进行四舍五入后 结果始终是正确的 但是usually四舍五入最后一位小数后
  • CamcorderProfile.videoCodec 返回错误值

    根据docs https developer android com reference android media CamcorderProfile html 您可以使用CamcorderProfile获取设备默认视频编解码格式 然后将其
  • 使用 svn 1.8.x、subclise 1.10 的 m2e-subclipse 连接器在哪里?

    我读到 m2e 的生产商已经停止生产 svn 1 7 以外的任何版本的 m2e 连接器 Tigris 显然已经填补了维护 m2e subclipse 连接器的空缺 Q1 我的问题是 使用 svn 1 8 x 的 eclipse 更新 url
  • 双枢轴快速排序和快速排序有什么区别?

    我以前从未见过双枢轴快速排序 是快速排序的升级版吗 双枢轴快速排序和快速排序有什么区别 我在 Java 文档中找到了这个 排序算法是双枢轴快速排序 作者 弗拉基米尔 雅罗斯拉夫斯基 乔恩 本特利和约书亚 布洛赫 这个算法 在许多数据集上提供
  • Spring Rest 和 Jsonp

    我正在尝试让我的 Spring Rest 控制器返回jsonp但我没有快乐 如果我想返回 json 但我有返回的要求 完全相同的代码可以正常工作jsonp我添加了一个转换器 我在网上找到了用于执行 jsonp 转换的源代码 我正在使用 Sp

随机推荐

  • 9月13号魔兽服务器维护,魔兽世界怀旧服9月13日7点再次开放免费转服 11个转服新服务器一览...

    魔兽世界怀旧服官方在9月12日的中午发布公告 表示将会在9月13日 也就是中秋节的上午7点到9月16日的早上7点再次开放免费转服 其中新开放了11组新服务器 具体的内容如下 我们预计于 9 月 13 日07 00 至 9 月 16 日07
  • Qt 5界面修改无效的问题解决QtDesigner修改之后无效的解决办法

    第一篇 关于Qt Ui修改不起作的结局办法 我在网上找到的解决办法 ui源文件到界面显示的原理可以网上搜索 这里不再描述 简单讲就是先要从 ui生成ui h然后再编译 所以界面未更新实际上是因为ui h这个文件没有更新导致的 根治的方法 项
  • Web Spider 常见混淆EVAL、AA、JJ、JSFUCK

    文章目录 一 资源推荐 二 EVAL constructor 三 AA混淆 四 JJ混淆 五 JSFUCK 六 针对混淆的方法 简单hook 总结 混淆的原理 就是把原本清晰的代码故意搞得花里胡哨 让局外人看不懂 混淆的目的 让逆向者很难理
  • Python中使用input()无输入

    Python中使用input 无输入 sublime Text编译器因页面美观 插件众多 功能强大深受编程爱好者的青睐 然而在编程中由于插件忘记安装导致程序运行失败 使人颇为烦恼 问题描述 在sublime Text编译器中使用input
  • Git 如何压缩 commit

    今天同事突然问我 由于在给老大的开源项目提 pr 的时候 自己比较长时间没有 rebase 的老大的项目 master 分支了 而自己提交的 commit 又很多 有些 commit 又是实验性质的 乱七八糟的 其实就是没有用熟 git 还
  • Java工作流框架:探索流程引擎的实现和应用

    目前 市面上有很多基于SpringBoot Vue前后端分离的Java快速开发框架和工作流开发框架可供选择 以下是一些比较流行的框架 1 Spring Cloud Spring Cloud是一套基于Spring Boot的开发工具 用于快速
  • sqli-labs Less18 原理到实现详解

    目录 前期知识储备 一 初始思路 1 思路 2 user agent后加入 发现出现sql错误日志 3 我尝试了之前用的各种闭合方法 都已失败告终 4 新的问题 5 现在想的是如何利用mysql插入语句insert实现注入 前期知识储备 从
  • 【Qt styleSheet样式表】

    一 Qt styleSheet样式表语法 1 属性列表 下表列出了 Qt 样式表支持的所有属性 可以为属性赋予哪些值取决于属性类型 除非另有说明 否则以下属性适用于所有小部件 标有星号 的属性为Qt特定的 在 CSS2 或 CSS3 中没有
  • Python+PySide2:使用多线程处理界面卡死的情况

    本篇主要是梳理一下 在使用 Pyside2 模块的时候 利用多线程处理页面假死 exe未响应 的问题 一 这边举个例子吧 如下图所示 测试代码如下 class Stats def init self 从文件中加载UI定义 super ini
  • 32位 与64位编译

    为了适应现在越来越流行的64位系统 经常需要将代码分别编译为32位版和64位版 其次 除了需要生成debug版用于开发测试外 还需要生成release版用于发布 本文介绍了如何利用makefile条件编译来生成这些版本 而且不仅兼容Linu
  • 1、python调用java的jar包

    在python中调用jar包 最近的项目功能需要调用客户的java接口 在调用接口的时候需要使用配套的jar包生成一些参数 但是公司的项目是用django搭建的 经过无数血与泪的尝试 最终终于找到了合适的方法去调用 jpype包是一个Pyt
  • 解决dubbo问题:forbid consumer

    原文地址 http www jameswxx com e4 b8 ad e9 97 b4 e4 bb b6 e8 a7 a3 e5 86 b3dubbo e9 97 ae e9 a2 98 ef bc 9aforbid consumer 线
  • Win10环境python3.7安装dlib模块

    自己按照别人的博客安装了一下dlib 测试成功 在这里自己也稍微总结一下 博客原文 https blog csdn net zhang475546259 article details 84104368 1 安装VS201x 全称visua
  • 简单支付验证-SPV

    SPV的来源 在比特币整个生态圈里 大部分都是普通用户 即只有基本的比特币投资及消费支付需要的用户 他们可能没有矿机 没有高端配置的电脑 那么他们是否也要运行一个全节点程序呢 要知道 现在官方显示的结果 比特币所有区块数据目前在120G左右
  • webstorm等JetBrains全家桶搜索任何插件都无反应

    使用webstorm搜索任何插件都无反应 打开webstorm设置 搜索settings或者是System Settings 找到HTTP Proxy 设置AUTO detect proxy settings后 点击右下角的Apply应用后
  • vue 中provide的用法_Vue中使用provide和inject

    相信大家在工作中一定遇到过多层嵌套组件 而vue 的组件数据通信方式又有很多种 比如vuex dollar parent与 dollar children prop dollar emit与 dollar on dollar attrs与
  • 为什么需要对数值类型的特征做归一化?

    为什么需要对数值类型的特征做归一化 1 举例子 比如分析一个人的身高和体重对健康的影响 身高的单位是m 范围是1 6 1 8 体重的单位是kg 在50kg 100kg之间 分析出的结果自然会倾向于数值差异较大的体重特征 因此我们需要数值归一
  • 新版Spring Boot(10)- Spring Boot 整合数据持久层(1)

    1 整合JdbcTemplate Service Description TODO Author tzb Date 2021 8 22 10 23 Version 1 0 Service public class UserService A
  • 【Mysql】删除表记录,并限制条数

    删除表数据 好删 那如果要限制条数 如何删除呢 例如 有个表tag list 我要删除aid为6666的前100条数据 sql如下 删除表记录limit限制条数 delete from tag list where aid 6666 lim
  • JUC 六. 线程中断 与 LockSupport

    目录 一 基础理解 如何退出一个线程 volatile 与 AtomicBoolean 中断线程示例 Thread中自带的中断api示例 阻塞状态线程中断时异常解决 二 Thread中自带的中断底层分析 三 总结 一 基础理解 先了解几个问