【JUC】Java并发编程从挖坑到入土全解(4-一文讲通LockSupport与线程中断->长图预警)
目录
LockSupport与线程中断
线程中断机制
什么是中断机制?
与中断相关的3个API
如何停止中断运行中的线程?
当前线程的中断标识为true,是不是线程就会立刻停止?
如何理解静态方法Thread.interrupted()
LockSupport是什么
线程等待和唤醒机制
3种让线程等待唤醒的方法
Object类中的wait()和notify()方法实现线程的等待和唤醒
Condition接口中的await()后signal()方法实现线程的等待和唤醒
上述两个对象Object和Condition使用的限制条件
LockSupport类中的park()等待和unpack()唤醒
总结
LockSupport与线程中断
线程中断机制
什么是中断机制?
首先,一个线程不应该由其他线程强制中断或停止,而是应该由线程自己自行停止,自己来决定自己的命运(所以,Thread的stop()、suspend()、resume()都已经废弃了)
其次,在Java中没有办法立即停止一条线程,然而停止线程又显得那么重要(比如需要取消一个耗时/错误操作)。因此,Java提供了一种用于停止线程的协商机制——中断,也即中断标识协商机制。
中断只是一种协作协商机制,Java没有给中断增加任何语法,中断的过程完成完全要求程序员自己实现。若要中断一个线程,需要手动调用该线程的interrupt()方法,该方法也仅仅是将线程对象的中断标识设成true,接着按自己的需要,写代码不断监测当前线程的标识位,如果为true,表示别的线程请求这条线程中断,此时究竟该做什么需要自己写代码实现。
每个线程对象中都有一个中断标识位,用于表示线程是否被中断:该标识位为true标识中断,为false表示未中断。通过调用线程对象的interrupt方法将该线程的标识位设为true,可以在别的线程中带调用,也可以在自己的线程中调用。
与中断相关的3个API
- public void interrupt():设置线程的中断状态为true,发起一个协商而不会立刻停止线程
- public static boolean isInterrupted():通过检查中断标识位判断线程是否被中断并清除当前中断状态(返回当前线程的中断状态+重置中断状态为false)
- public boolean isInterrupted():通过检查中断标识位判断当前线程是否被中断
如何停止中断运行中的线程?
- 通过volatile变量实现(线程间的可见性)
- 通过AtomicBoolean实现
执行结果如下图:

- 通过Thread类自带的中断API实例方法实现 在需要中断的线程中不断监听中断状态,一旦发生中断,就执行相应的中断处理业务逻辑stop线程
-
- interrupt() 中断线程

我们可以看到interrupt0()是一个native方法

除非线程正在中断(始终允许),否则将带调用此线程的checkAccess()方法(可能还会导致抛出SecurityException)

public class InterruptDemo {static volatile boolean isStop = false;public static void main(String[] args) {Thread tA = new Thread(() -> {while (true) {if (isStop) {System.out.printf(Thread.currentThread().getName() + " isStop 被修改为true!");break;}System.out.println("-------------------->");}}, "tA");tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {isStop = true;}, "B").start();}
}
执行结果如下图:

public class InterruptDemo {static AtomicBoolean atomicBoolean = new AtomicBoolean(false);public static void main(String[] args) {Thread tA = new Thread(() -> {while (true) {if (atomicBoolean.get()) {System.out.printf(Thread.currentThread().getName() + " atomicBoolean 被修改为true!");break;}System.out.println("-------------------->");}}, "tA");tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {atomicBoolean.set(true);}, "B").start();}
}
package com.aqin.juc;import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;/*** @Description* @Author aqin1012 AQin.* @Date 10/9/23 11:29 AM* @Version 1.0*/
public class InterruptDemo {public static void main(String[] args) {Thread tA = new Thread(() -> {while (true) {if (Thread.currentThread().isInterrupted()) {System.out.printf(Thread.currentThread().getName() + " isInterrupted() 被修改为true!程序停止!");break;}System.out.println("-------------------->");}}, "tA");tA.start();try {TimeUnit.MILLISECONDS.sleep(20);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {tA.interrupt();}, "B").start();}
}
执行效果如下图:

当前线程的中断标识为true,是不是线程就会立刻停止?
不会,具体来说,对一个线程,调用interrupt()时,如果线程处于正常活动状态,那么会将该线程的中断标志设置为true(仅此而已),被设置中断标志的线程将继续正常运行,不受影响。所以,interrupt()并不是真正的中断线程,需要被调用的线程自己进行配合才行。如果线程处于被阻塞状态(如sleep、wait、join等状态),在别的线程中调用当前线程对象的interrupt()方法时,那么线程将会立即退出被阻塞状态,并抛出一个InterruptedException异常。
如何理解静态方法Thread.interrupted()

这个方法是判断线程是否中断并清除当前中断状态的,简单来说就是“复位”。
主要做了两件事:
- 返回当前线程的中断状态,测试当前线程是否已经被中断
- 将当前线程的中断状态清零并重新设置为false,清除线程的中断状态
那么问题来了,这个静态方法interrupted()跟实例方法isInterrupted()有什么区别呢?
我们举个简单的例子对比下执行结果
静态方法interrupted()
执行结果如下:

实例方法isInterrupted()
执行结果如下

来看它俩的源码对比

可以看到其实它们调用的是同一个方法,只不过,在对于是否需要清理这个参数的传递上,静态方法传递的是true,而实例方法传递的是false,简单来讲就是多了一步“复位”操作。因此,才会出现上面两段示例代码执行的结果不一致,在当前线程未执行中断方法interrupt()时,当前线程的中断标识位本身就是初始值false,因此连续调用两次静态方法或者实例方法的执行结果都是false;当当前线程执行了中断方法interrupt()时,第一次调用静态方法和实例方法的返回值同样都是true,但是此时静态方法多做了一步“复位”操作,把当前线程的中断标识位重置回了初始值false,而实例方法则没有这步操作,因此,当第二次调用时,实例方法的示例中当前线程的中断标识位仍然是true,因此仍然返回true,而静态方法的示例代码中当前线程的中断标识位已经被重置回了false,于是就返回了false。
LockSupport是什么
LockSupport是java.util.concurrent.locks中的一个类,用于创建锁和其他同步类的基本线程阻塞原语。
线程等待和唤醒机制
3种让线程等待唤醒的方法
- 使用Object中的wait()方法让线程等待,使用notify()方法唤醒线程
- 使用JUC包中的Condition的await()方法让线程等待,使用signal()方法唤醒线程
- 使用LockSupport类中的park()和unpark()方法阻塞当前线程以及唤醒指定被阻塞的线程
Object类中的wait()和notify()方法实现线程的等待和唤醒
示例代码:
public static void main(String[] args) {Object objectLock = new Object();new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}synchronized (objectLock) {System.out.println(Thread.currentThread().getName() + " 进入--->");try {objectLock.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 被唤醒^ ^");}}, "A").start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {synchronized (objectLock) {objectLock.notify();}System.out.println(Thread.currentThread().getName() + " 发出唤醒通知( ̄∇ ̄)/");}, "B").start();
}
执行结果如下:

问题:
- 必须要先持有锁,否则会报错

- 必须先wait()再notify(),否则会卡死

Condition接口中的await()后signal()方法实现线程的等待和唤醒
示例代码:
public static void main(String[] args) {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();new Thread(() -> {try { TimeUnit.SECONDS.sleep(1); } catch ( InterruptedException e) { e.printStackTrace();}lock.lock();System.out.println(Thread.currentThread().getName() + " 进入--->");try {condition.await();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}System.out.println(Thread.currentThread().getName() + " 被唤醒^ ^");}, "A").start();try { TimeUnit.SECONDS.sleep(1); } catch ( InterruptedException e) { e.printStackTrace();}new Thread(() -> {lock.lock();try {condition.signal();} finally {lock.unlock();}System.out.println(Thread.currentThread().getName() + " 发出唤醒通知( ̄∇ ̄)/");}, "B").start();}
执行结果如下:

问题:
- 必须要先持有锁,否则会报错

在lock、unlock对里面才能正确调用condition中线程等待和唤醒的方法。
- 必须先await()再signal(),否则会卡死

上述两个对象Object和Condition使用的限制条件
- 线程先要获得并持有锁,必须在锁块(synchronized/lock)中
- 必须要先等待后唤醒,线程才能够被唤醒
LockSupport类中的park()等待和unpack()唤醒
LockSupport类使用了一种名为Permit(许可)的概念来做到阻塞和唤醒线程的功能,每一个线程都有一个Permit(许可),但与Semaphore不同的是,许可的累加上限是1(最多一个许可)。
使用示例:
public static void main(String[] args) {Thread A = new Thread(() -> {try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + " 进入--->");LockSupport.park();System.out.println(Thread.currentThread().getName() + " 被唤醒^ ^");}, "A");A.start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(() -> {LockSupport.unpark(A);System.out.println(Thread.currentThread().getName() + " 发出唤醒通知( ̄∇ ̄)/");}, "B").start();
}
执行结果如下:

可以看到,使用LockSupport进行线程阻塞/唤醒不需要在锁对块(synchronized/lock)中,所以上面Object类和Condition接口的第一个问题解决了,然后我们看看第二个问题:加锁/解锁的先后顺序。

如上图可以看出,park()和unpack()执行的先后顺序不会影响结果。
总结
LockSupport是用来创建锁和其他同步类的基本线程阻塞原语。
LockSupport是一个线程阻塞工具,所有的方法都是静态方法,可以让线程在任意位置阻塞,阻塞后也有对应的唤醒方法,归根结底,LockSupport调用的是Unsafe中的native代码。

LockSupport提供park()和unpark()方法实现阻塞线程和解除线程阻塞的过程,每个使用LockSupport的线程都一个Permit(许可)与LockSupport相关联,每一个线程都都一个相关的Permit,并且最多一个,重复调用unpark()也不会积累Permit。
换句话讲,线程阻塞需要消耗凭证,并且这个凭证最多1个
当调用park()方法时:
- 如果有Permit,会消耗这个Permit,然后正常退出
- 如果没有Permit,会阻塞当前线程等待获取凭证
当调用unpark()方法时:
- 会增加一个Permit
- 调用多次也只会有一个Permit(不累加)
最后,我们思考2个问题:
- 为什么LockSupport提供park()和unpark()方法可以突破Object和Condition的调用先后顺序的限制? 因为调用unpark()会获得1个Permit,之后再调用park()会消耗这个Permit,park()和unpark()方法的执行顺序不会影响线程的唤醒,即便先发放了Permit仍然不会阻塞线程。
- 唤醒两次(调用两次park()方法)后阻塞两次(调用两次unpark)方法),会发生什么情况? 线程会被阻塞,因为Permit不会累加,即使调用了两次unpark(),仍然只会有一个Permit,后面接着的调用两次park(),在调用第一次park()时,就会把这个Permit消耗掉,第二次调用时就没有Permit了,因而会被阻塞。
搞定( ̄∇ ̄)/🎉🎉🎉~~~~~~~~~~
相关文章:
【JUC】Java并发编程从挖坑到入土全解(4-一文讲通LockSupport与线程中断->长图预警)
目录 LockSupport与线程中断 线程中断机制 什么是中断机制? 与中断相关的3个API 如何停止中断运行中的线程? 当前线程的中断标识为true,是不是线程就会立刻停止? 如何理解静态方法Thread.interrupted() LockSupport是什么…...
Springboot学习笔记——3
Springboot学习笔记——3 一、热部署1.1、手动启动热部署1.2、自动启动热部署1.3、热部署范围配置1.4、关闭热部署 二、配置高级2.1、第三方bean属性绑定2.2、松散绑定2.3、常用计量单位应用2.4、bean属性校验2.5、进制数据转换规则 三、测试3.1、加载测试专用属性3.2、加载测试…...
jupyter 切换虚拟环境
当前只有两个环kernel 我已经创建了很多虚拟环境,如何在notebook中使用这些虚拟环境呢?请看下面 比如说我要添加nlp 这个虚拟环境到notebook中 1. 切换到nlp环境 2. 安装如下模块 pip install ipykernel 3. 执行如下命令 python -m ipykernel install …...
如何在Apache和Resin环境中实现HTTP到HTTPS的自动跳转:一次全面的探讨与实践
🌷🍁 博主猫头虎 带您 Go to New World.✨🍁 🦄 博客首页——猫头虎的博客🎐 🐳《面试题大全专栏》 文章图文并茂🦕生动形象🦖简单易学!欢迎大家来踩踩~🌺 &a…...
安全防御—密码学
1. 什么是APT? APT(Advanced Persistent Threat)是指高级持续性威胁,本质是针对性攻击。 利用先进的攻击手段对特定目标进行长期持续性网络攻击的攻击形式,APT攻击的原理相对于其他攻击形式更为高级和先进,…...
灯具从深圳寄国际物流到墨西哥
在国际贸易的日益频繁的今天,越来越多的企业开始将产品销往海外市场。然而,如何将这些产品安全、快速地送达目的地,成为了每个企业都需要面对的问题。对于灯具这种重量大、体积大的物品来说,如何选择合适的国际物流方式࿰…...
spark3使用hive zstd压缩格式总结
ZSTD(全称为Zstandard)是一种开源的无损数据压缩算法,其压缩性能和压缩比均优于当前Hadoop支持的其他压缩格式,本特性使得Hive支持ZSTD压缩格式的表。Hive支持基于ZSTD压缩的存储格式有常见的ORC,RCFile,Te…...
直线导轨精度等级在设备中有什么影响?
直线导轨的精度选择是直线导轨应用中的重要环节,需要根据具体的应用场景和设备要求来选择合适的精度等级(常见分3个等级:N/H/P)。下面我们来详细了解一下直线导轨的精度选择。 1、精度等级的概念:直线导轨的精度等级是…...
windows平台FairMOT的实现
环境:python3.6pytorch1.1.0torchvision0.3.0cuda9.2vs2015 该项目需要装3个c库(dcn_v2,apex,cython_bbox)特别坑,各种环境不匹配,各种bug。本人c小白,但是一路摸索总算成功了。下面…...
系统架构设计:12 论软件维护方法及其应用
目录 一 软件维护方法 1 影响软件维护工作的因素 2 软件维护类型 (1)正确性维护...
SS命令使用介绍
ss 一. 命令介绍 先使用手册查看命令介绍信息 NAME ss - another utility to investigate sockets DESCRIPTION ss is used to dump socket statistics. It allows showing information similar to netstat. It can display more TCP and state informations than other tools.…...
让你的对象变得拗口:JSON.stringify(),我把对象夹进了 JSON 魔法帽!
🎬 江城开朗的豌豆:个人主页 🔥 个人专栏 :《 VUE 》 《 javaScript 》 📝 个人网站 :《 江城开朗的豌豆🫛 》 ⛺️ 生活的理想,就是为了理想的生活 ! 目录 引言 1. JSON.stringify() 属性 replacer …...
TDengine时序数据库学习使用
数据库要求: 1.目前服务器只能在linux运行,先安装服务器版本v1 2.下载与v1完全匹配的客户端版本v1(客户端与服务器的版本号不匹配可能访问不了服务器) 第一步 安装 安装服务器注意,安装教程: 使用安装…...
算法通过村第十三关-术数|青铜笔记|数字与数学
文章目录 前言数字统计专题符号统计阶乘0的个数 溢出问题整数反转字符串转整数回文数 进制专题七进制数进制转换 总结 前言 提示:生活是正着来生活,倒着去理解。 --戴维迈尔斯《社会心理学》 数学是学生时代掉头发的学科,那算法是毕业后掉头发…...
【SpringMVC篇】详解SpringMVC入门案例
🎊专栏【SpringMVC】 🍔喜欢的诗句:天行健,君子以自强不息。 🎆音乐分享【如愿】 🎄欢迎并且感谢大家指出小吉的问题🥰 文章目录 🎍SpringMVC简介⭐优点 🌺SpringMVC入门…...
Programming abstractions in C阅读笔记:p166-p175
《Programming Abstractions In C》学习第58天,p166-p175总结。 一、技术总结 1.斐波那契数列(Fibonacci Sequenc) (1)斐波那契数列来源 斐波那契数列来自于《Liber Abaci》一书里兔子繁殖问题,相关资料很多,这里不赘述。 (2)关于《Libe…...
【List-Watch】
List-Watch 一、定义二、工作机制三、调度过程 一、定义 Kubernetes 是通过 List-Watch 的机制进行每个组件的协作,保持数据同步的,每个组件之间的设计实现了解耦。 用户是通过 kubectl 根据配置文件,向 APIServer 发送命令,在 …...
Pytorch因nn.Parameter导致实验不可复现的一种情况
文章首发见博客:https://mwhls.top/4871.html。 无图/格式错误/后续更新请见首发页。 更多更新请到mwhls.top查看 欢迎留言提问或批评建议,私信不回。 没解决,只是记录这种情况。 也可以多次实验取均值以避免结果复现。 场景 自己的模块中&a…...
MySQL表名区分不区分大小写,规则是怎样
MySQL表名区分不区分大小写,规则是怎样 mysql在linux中表名区分大小写,mysql在Windows中表名不区分大小写;可以在MySQL的配置文件“my.ini [mysqld]”中增加一行“lower_case_table_names 参数”来设置是否区分大小写。 mysql的表名区分大小写…...
Design patterns--观察者模式
设计模式之观察者模式 代码示例 #ifndef OBSERVER_H #define OBSERVER_H#include <map>class Observer { public:Observer();virtual void update(std::map<int, double>) 0; }; #endif // OBSERVER_H#include "observer.h"Observer::Observer() {}#if…...
docker详细操作--未完待续
docker介绍 docker官网: Docker:加速容器应用程序开发 harbor官网:Harbor - Harbor 中文 使用docker加速器: Docker镜像极速下载服务 - 毫秒镜像 是什么 Docker 是一种开源的容器化平台,用于将应用程序及其依赖项(如库、运行时环…...
DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
React Native在HarmonyOS 5.0阅读类应用开发中的实践
一、技术选型背景 随着HarmonyOS 5.0对Web兼容层的增强,React Native作为跨平台框架可通过重新编译ArkTS组件实现85%以上的代码复用率。阅读类应用具有UI复杂度低、数据流清晰的特点。 二、核心实现方案 1. 环境配置 (1)使用React Native…...
cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
华为OD机试-食堂供餐-二分法
import java.util.Arrays; import java.util.Scanner;public class DemoTest3 {public static void main(String[] args) {Scanner in new Scanner(System.in);// 注意 hasNext 和 hasNextLine 的区别while (in.hasNextLine()) { // 注意 while 处理多个 caseint a in.nextIn…...
unix/linux,sudo,其发展历程详细时间线、由来、历史背景
sudo 的诞生和演化,本身就是一部 Unix/Linux 系统管理哲学变迁的微缩史。来,让我们拨开时间的迷雾,一同探寻 sudo 那波澜壮阔(也颇为实用主义)的发展历程。 历史背景:su的时代与困境 ( 20 世纪 70 年代 - 80 年代初) 在 sudo 出现之前,Unix 系统管理员和需要特权操作的…...
优选算法第十二讲:队列 + 宽搜 优先级队列
优选算法第十二讲:队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...
python报错No module named ‘tensorflow.keras‘
是由于不同版本的tensorflow下的keras所在的路径不同,结合所安装的tensorflow的目录结构修改from语句即可。 原语句: from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后: from tensorflow.python.keras.lay…...
免费PDF转图片工具
免费PDF转图片工具 一款简单易用的PDF转图片工具,可以将PDF文件快速转换为高质量PNG图片。无需安装复杂的软件,也不需要在线上传文件,保护您的隐私。 工具截图 主要特点 🚀 快速转换:本地转换,无需等待上…...
协议转换利器,profinet转ethercat网关的两大派系,各有千秋
随着工业以太网的发展,其高效、便捷、协议开放、易于冗余等诸多优点,被越来越多的工业现场所采用。西门子SIMATIC S7-1200/1500系列PLC集成有Profinet接口,具有实时性、开放性,使用TCP/IP和IT标准,符合基于工业以太网的…...
