【JUC】十五、中断协商机制

news2024/11/20 14:23:37

文章目录

  • 1、线程中断机制
  • 2、三大中断方法的说明
  • 3、通过volatile变量实现线程停止
  • 4、通过AtomicBoolean实现线程停止
  • 5、通过Thread类的interrupt方法实现线程停止
  • 6、interrupt和isInterrupted方法源码
  • 7、interrupt方法注意点
  • 8、静态方法interrupted的注意点

1、线程中断机制

一个线程不应该由其他线程来强制停止,而应该由线程自己来决定是否停止,因此:

//已过时
Thread.stop
Thread.suspend
Thread.resume

这三个方法已废弃。但如果一个线程确实需要停止,比如取消一个耗时任务,因此针对停止线程,Java提供中断标识协商机制。关键字:

  • 自己决定
  • 中断标识
  • 只是协商,不是强制,不会里了停止线程,是我希望你能停止

最后,中断这种协商机制,Java没有给具体语法或者关键字,中断逻辑是开发者自己根据实际需求实现的。每个线程对象都有一个中断标识位,true表示中断,false未中断。此时,写代码去不断检测当前线程的标识位,检测到true,则表示其他线程请求中断这条线程(注意,只是代表别人请求中断)。

因此,想中断一个线程,可手动调用该线程的interrupt方法(可以自己调,也可以别的线程调),该方法仅仅是将线程对象的中断标识设为true。

2、三大中断方法的说明

搜索java.lang.Thread类下的方法,关于中断有:
在这里插入图片描述

1public void interrupt()
  • 特点:实例方法,作用于调用对象线程
  • 作用:仅仅将线程调用对象线程的中断状态设置为true(注意是协商,只是标志位变了,线程不会立刻停止)
2public static boolean interrupted()
  • 特点:静态方法,作用于当前线程
  • 作用:a、返回当前线程的中断状态 b、将当前线程的中断状态重新设为false ,类似i++,先返回i,再做自增1
3public boolean isInterrupted()
  • 特点:实例方法,作用于调用对象线程
  • 作用:看看线程的中断标志位是true还是false

3、通过volatile变量实现线程停止

引入一个自定义的标志位:

public class InterruptDemo1 {

    static volatile boolean isStop = false;

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

        new Thread(() -> {
            while (true) {
                if (isStop) {
                    System.out.println("标识位为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "正在运行任务...");
            }
        }, "t1").start();
        //20ms后启动线程t2,去把自定义的标志位改为true
        TimeUnit.MILLISECONDS.sleep(20);
        new Thread(() -> {
            isStop = true;
        }, "t2").start();
    }
}

在这里插入图片描述

4、通过AtomicBoolean实现线程停止

同引入一个自定义标志位:

public class InterruptDemo1 {

    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

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

        new Thread(() -> {
            while (true) {
                if (atomicBoolean.get()) {
                    System.out.println("标识位为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "正在运行任务...");
            }
        }, "t1").start();
        //20ms后启动线程t2,去把自定义的标志位改为true
        TimeUnit.MILLISECONDS.sleep(20);
        new Thread(() -> {
            atomicBoolean.set(true);
        }, "t2").start();
    }
}

5、通过Thread类的interrupt方法实现线程停止

使用:

  • Thread.currentThread().isInterrupted()查看当前线程标志位
  • t1.interrupt改变标志位
public class InterruptDemo1 {

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

        Thread t1 = new Thread(() -> {
            System.out.println("当前线程的默认中断标志位是:" + Thread.currentThread().isInterrupted());
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("标识位为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "正在运行任务...");
            }
        }, "t1");
        t1.start();
        //20ms后启动线程t2,去把自定义的标志位改为true
        TimeUnit.MILLISECONDS.sleep(20);
        new Thread(() -> {
            t1.interrupt();
        }, "t2").start();
    }
}

自己来中断:

public class InterruptDemo1 {

    static AtomicBoolean atomicBoolean = new AtomicBoolean(false);

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

        Thread t1 = new Thread(() -> {
            System.out.println("当前线程的默认中断标志位是:" + Thread.currentThread().isInterrupted());
            int i = 1;
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("标识位为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "正在运行任务...");
                i++;
                if (i == 6) {
                    //自己来改标志位
                    Thread.currentThread().interrupt();
                }
            }
        }, "t1");
        t1.start();
        
    }
}

在这里插入图片描述

6、interrupt和isInterrupted方法源码

interrupt方法源码:checkAccess方法往下可能抛出SecurityException异常,即当前线程无法修改此线程

在这里插入图片描述

不管调用interrupt的线程对象是不是当前线程对象,都调了interrupt0,跟进interrupt0,native即说明这里是JVM在调底层操作系统或者第三方C的函数库

在这里插入图片描述

isInterrupted方法,底层调一个native方法,中断状态是否重置取决于传的值,isInterrupt传false,因此isInterrupted方法只是看看线程对象的标志位是啥。

在这里插入图片描述

7、interrupt方法注意点

当对一个线程t 调用interrupt方法:

  • 如果线程t 处于正常活动状态,则将其标志位设置为true,仅此而已,t线程继续正常运行,不受影响
  • 如果线程t 调用了wait、join、sleep方法而被阻塞,调用interrupt就会清除中断状态,并抛出InterruptedException异常
  • 如果线程t 已经处于不活动状态,或者说是run方法运行完,进入死亡状态了,那中断这个线程不会产生任何影响

先看下中断一个不活动的线程:

public class InterruptDemo2 {

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

        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 300; i++) {
                System.out.println("----" + i);
            }
        }, "t1");
        t1.start();
        System.out.println("t1的默认中断标志位:" + t1.isInterrupted());
        TimeUnit.MILLISECONDS.sleep(2);
        t1.interrupt();
        System.out.println("main线程中更改了t1的标志位,结果:" + t1.isInterrupted());
        TimeUnit.MILLISECONDS.sleep(2000);
        System.out.println("t1线程进入死亡状态后再次获取其标志位,结果:" + t1.isInterrupted());
    }
}

在这里插入图片描述
在这里插入图片描述

此时,t1线程已经属于不活动的线程,或者说run方法执行结束了,线程进入死亡状态,此时标志位为false。连续重复中断,或者线程进入死亡状态后再改中断标志位等操作,做了等于没做。

再试试中断一个wait/join/sleep被阻塞的线程:

public class Interrupt3 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("标识位变更为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                //注意这里!
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {              
                    e.printStackTrace();
                }
                System.out.println("----running...");
            }
        }, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);
        //在main线程中改t1的标志位
        t1.interrupt();
    }
}

在这里插入图片描述

捕捉到异常catch处理时再调一次interrupt:

public class Interrupt3 {

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true) {
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("标识位变更为true,有线程请求停止本线程,即将主动停止,保存数据中....");
                    break;
                }
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    //异常被捕捉后,再次将标志位置为true
                    Thread.currentThread().interrupt();     //!!!##
                    e.printStackTrace();
                }
                System.out.println("----running...");
            }
        }, "t1");
        t1.start();
        TimeUnit.SECONDS.sleep(1);
        //在main线程中改t1的标志位
        t1.interrupt();
    }
}

在这里插入图片描述

为什么要在异常处再调一次interrupt?
  • 中断标志位默认为false
  • 正常情况下,调用t1.interrrupt,标志位改为true
  • 线程调用了sleep/join/wait且处于阻塞状态时,调用t1.interrrupt,抛出InterruptionException,且重置标志位为false,进而导致循环继续,线程未成功中止
  • 所以须在catch块中,再次设置中断标志位为true才行

源码中已有说明:

在这里插入图片描述

8、静态方法interrupted的注意点

前面提到,静态方法interrupted:

  • a、返回当前线程的中断状态
  • b、将当前线程的中断状态重新设为false ,类似i++,先返回i,再做自增1

写个Demo验证下

public class Interrupt4 {

    public static void main(String[] args) {

        System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
        System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
        System.out.println("-------1");
        Thread.currentThread().interrupt();  //中断标志位改为true
        System.out.println("-------2");
        System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
        System.out.println(Thread.currentThread().getName() + "\t" + Thread.interrupted());
    }
}

结果:
在这里插入图片描述

看下源码:

在这里插入图片描述

interrupted和isInterrupted这两个方法底层调用的都是同一个方法(native),区别是传的值不同,静态方法传了true,即清理了中断标志位,实例方法isInterrupted则不会,因为传了false。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1269458.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

二叉树leetcode(求二叉树深度问题)

today我们来练习三道leetcode上的有关于二叉树的题目&#xff0c;都是一些基础的二叉树题目&#xff0c;那让我们一起来学习一下吧。 https://leetcode.cn/problems/maximum-depth-of-binary-tree/submissions/ 看题目描述是让我们来求出二叉树的深度&#xff0c;我们以第一个父…

Drawer抽屉(antd-design组件库)简单用法

1.Drawer抽屉 屏幕边缘滑出的浮层面板。 2.何时使用 抽屉从父窗体边缘滑入&#xff0c;覆盖住部分父窗体内容。用户在抽屉内操作时不必离开当前任务&#xff0c;操作完成后&#xff0c;可以平滑地回到原任务。 需要一个附加的面板来控制父窗体内容&#xff0c;这个面板在需要时…

python取百分位数据、ENVI数据归一化

1、python取百分位数据 两种取值方法 1&#xff09;取值会计算百分比数、会产生小数&#xff0c;该数可能不是数据里的 import numpy as npdata [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]# 计算百分位数 percentiles np.percentile(data, [5, 95]) min_percentile percentiles[0]…

[笔记] 使用 xshell 记录日志

平常会使用xshell登录远程系统,在一些场景下,由于远端节点不支持下载,因此无法下载日志,此时可以通过 xshell 自带的日志功能将远端节点的日志内容导出. 1. 登录远端节点后启动日志记录 2. 指定要保存的日志文件 3. 在终端中使用 cat /path/to/logfile 将文件内容全部打印到终…

Ubuntu 环境下 NFS 服务安装及配置使用

需求&#xff1a;公司内部有多台物理服务器&#xff0c;需要A服务器上的文件让B服务器访问&#xff0c;也就是两台服务器共享文件&#xff0c;当然也可以对A服务器上的文件做权限管理&#xff0c;让B服务器只读或者可读可写 1、NFS 介绍 NFS 是 Network FileSystem 的缩写&…

正则表达式【C#】

1作用&#xff1a; 1文本匹配&#xff08;验证字符串&#xff09; 2查找字符串 2符号&#xff1a; . ^ $ * - ? ( ) [ ] { } \ | [0-9] 匹配出数字 3语法格式&#xff1a; / 表示模式 / 修饰符 /[0-9]/g 表示模式&#xff1a;是指匹配条件&#xff0c;要写在2个斜…

使用 OpenTelemetry 和 Golang

入门 在本文中&#xff0c;我将展示你需要配置和处理统计信息所需的基本代码。在这个简短的教程中&#xff0c;我们将使用 Opentelemetry 来集成我们的 Golang 代码&#xff0c;并且为了可视化&#xff0c;我们将使用 Jeager。 在开始之前&#xff0c;让我简要介绍一下什么是 …

某60物联网安全之IoT漏洞利用实操2学习记录

物联网安全 文章目录 物联网安全IoT漏洞利用实操2&#xff08;内存破坏漏洞&#xff09;实验目的实验环境实验工具实验原理实验内容实验步骤ARM ROP构造与调试MIPS栈溢出漏洞逆向分析 IoT漏洞利用实操2&#xff08;内存破坏漏洞&#xff09; 实验目的 学会ARM栈溢出漏洞的原理…

如何使用 CSS columns 布局来实现自动分组布局?

最近在项目中碰到这样一个布局&#xff0c;有一个列表&#xff0c;先按照 4 2 的正常顺序排列&#xff0c;当超过 8 个后&#xff0c;会横向重新开始 4 2 的布局&#xff0c;有点像一个个独立的分组&#xff0c;然后水平排列&#xff0c;如下 图中序号是 dom 序列&#xff0c;所…

使用Java对yaml和properties互转,保证顺序、实测无BUG版本

使用Java对yaml和properties互转 一、 前言1.1 顺序错乱的原因1.2 遗漏子节点的原因 二、优化措施三、源码 一、 前言 浏览了一圈网上的版本&#xff0c;大多存在以下问题&#xff1a; 转换后顺序错乱遗漏子节点 基于此进行了优化&#xff0c;如果只是想直接转换&#xff0c…

IDEA2022 Git 回滚及回滚内容恢复

IDEA2022 Git 回滚 ①选择要回滚的地方&#xff0c;右键选择 ②选择要回滚的模式 ③开始回滚 ④soft模式回滚的内容会保留在暂存区 ⑤输入git push -f &#xff0c;然后重新提交&#xff0c;即可同步远程 注意观察IDEA右下角分支的标记&#xff0c;蓝色代表远程内容未同步到本…

初识Java 18-5 泛型

目录 动态类型安全 异常 混型 C中的混型 替代方案 与接口混合 使用装饰器模式 与动态代理混合 本笔记参考自&#xff1a; 《On Java 中文版》 动态类型安全 在Java 5引入泛型前&#xff0c;老版本的Java程序中就已经存在了List等原生集合类型。这意味着&#xff0c;我们…

LeetCode(38)生命游戏【矩阵】【中等】

目录 1.题目2.答案3.提交结果截图 链接&#xff1a; 生命游戏 1.题目 根据 百度百科 &#xff0c; 生命游戏 &#xff0c;简称为 生命 &#xff0c;是英国数学家约翰何顿康威在 1970 年发明的细胞自动机。 给定一个包含 m n 个格子的面板&#xff0c;每一个格子都可以看成是…

Linux(fork+exec创建进程)

1.进程创建 内核设计与实现43页; 执行了3次ps -f ,ps -f的父进程的ID(PPID)都是一样的,即bash. 实际上Linux上这个bash就是不断的复制自身,然后把复制出来的用exec替换成想要执行的程序(比如ps); 运行ps,发现ps是bash的一个子进程;原因就是bash把自己复制一份,然后替换成ps;…

深度学习-模型调试经验总结

1、 这句话的意思是&#xff1a;期望张量的后端处理是在cpu上&#xff0c;但是实际是在cuda上。排查代码发现&#xff0c;数据还在cpu上&#xff0c;但是模型已经转到cuda上&#xff0c;所以可以通过把数据转到cuda上解决。 解决代码&#xff1a; tensor.to("cuda")…

vuepress-----7、发布在GitHub

# 7、发布在GitHub 在你的项目中&#xff0c;创建一个如下的 deploy.sh 文件&#xff08;请自行判断去掉高亮行的注释&#xff09;: #!/usr/bin/env sh# 确保脚本抛出遇到的错误 set -e# 生成静态文件 npm run docs:build# 进入生成的文件夹 cd docs/.vuepress/dist# 如果是发…

attention中Q,K,V的理解

第一种 1.首先定义三个线性变换矩阵&#xff0c;query&#xff0c;key&#xff0c;value&#xff1a; class BertSelfAttention(nn.Module):self.query nn.Linear(config.hidden_size, self.all_head_size) # 输入768&#xff0c; 输出768self.key nn.Linear(config.hidde…

python实验3 石头剪刀布游戏

实验3&#xff1a;石头剪刀布游戏 一、实验目的二、知识要点图三、实验1. 石头剪刀布2. 实现大侠个人信息 一、实验目的 了解3类基本组合数据类型。理解列表概念并掌握Python中列表的使用。理解字典概念并掌握Python中字典的使用。运用jieba库进行中文分词并进行文本词频统计。…

COGVLM论文解读(COGVLM:VISUAL EXPERT FOR LARGE LANGUAGE MODELS)

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、摘要二、引言三、模型方法1、模型思路2、融合公式 四、训练方法总结 前言 2023年5月18日清华&智谱AI发布并开源VisualGLM-6B以来&#xff0c;清华KEG&…

竞赛选题 题目:基于深度学习的中文对话问答机器人

文章目录 0 简介1 项目架构2 项目的主要过程2.1 数据清洗、预处理2.2 分桶2.3 训练 3 项目的整体结构4 重要的API4.1 LSTM cells部分&#xff1a;4.2 损失函数&#xff1a;4.3 搭建seq2seq框架&#xff1a;4.4 测试部分&#xff1a;4.5 评价NLP测试效果&#xff1a;4.6 梯度截断…