LockSupport

news/2024/9/7 23:29:00/文章来源:https://www.cnblogs.com/sprinining/p/18326997

LockSupprot 用来阻塞和唤醒线程,底层实现依赖于 Unsafe 类(后面会细讲)。

该类包含一组用于阻塞和唤醒线程的静态方法,这些方法主要是围绕 park 和 unpark 展开。

public class Main {public static void main(String[] args) {Thread mainThread = Thread.currentThread();// 当 counterThread 数到 10 时,它会唤醒 mainThread。而 mainThread 在调用 park 方法时会被阻塞,直到被 unpark。Thread counterThread = new Thread(() -> {for (int i = 1; i <= 20; i++) {System.out.println(i);if (i == 10) {// 当数到10时,唤醒主线程LockSupport.unpark(mainThread);}}});counterThread.start();// 主线程调用parkLockSupport.park();System.out.println("Main thread was unparked.");}
}

阻塞线程


  1. void park():阻塞当前线程,如果调用 unpark 方法或线程被中断,则该线程将变得可运行。请注意,park 不会抛出 InterruptedException,因此线程必须单独检查其中断状态。
  2. void park(Object blocker):功能同方法 1,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。
  3. void parkNanos(long nanos):阻塞当前线程一定的纳秒时间,或直到被 unpark 调用,或线程被中断。
  4. void parkNanos(Object blocker, long nanos):功能同方法 3,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。
  5. void parkUntil(long deadline):阻塞当前线程直到某个指定的截止时间(以毫秒为单位),或直到被 unpark 调用,或线程被中断。
  6. void parkUntil(Object blocker, long deadline):功能同方法 5,入参增加一个 Object 对象,用来记录导致线程阻塞的对象,方便问题排查。

唤醒线程


void unpark(Thread thread):唤醒一个由 park 方法阻塞的线程。如果该线程未被阻塞,那么下一次调用 park 时将立即返回。这允许“先发制人”式的唤醒机制。

实际上,LockSupport 阻塞和唤醒线程的功能依赖于 sun.misc.Unsafe,比如 LockSupport 的 park 方法是通过 unsafe.park() 方法实现的。

Dump 线程


"Dump 线程"通常是指获取线程的当前状态和调用堆栈的详细快照。这可以提供关于线程正在执行什么操作以及线程在代码的哪个部分的重要信息。

下面是线程转储中可能包括的一些信息:

  • 线程 ID 和名称:线程的唯一标识符和可读名称。
  • 线程状态:线程的当前状态,例如运行(RUNNABLE)、等待(WAITING)、睡眠(TIMED_WAITING)或阻塞(BLOCKED)。
  • 调用堆栈:线程的调用堆栈跟踪,显示线程从当前执行点回溯到初始调用的完整方法调用序列。
  • 锁信息:如果线程正在等待或持有锁,线程转储通常还包括有关这些锁的信息。

线程转储可以通过各种方式获得,例如使用 Java 的 jstack 工具,或从 Java VisualVM、Java Mission Control 等工具获取。

下面是一个简单的例子,通过 LockSupport 阻塞线程,然后通过 Intellij IDEA 查看 dump 线程信息。

public class LockSupportDemo {public static void main(String[] args) {LockSupport.park();}
}

先运行程序,再在 Run 面板中找到 attach to process,选择 attach 到主线程:

再到 debugger 面板中找到 export threads。

导出后就能看见线程信息了。

与 synchronized 的区别


synchronized 会使线程阻塞,线程会进入 BLOCKED 状态,而调用 LockSupprt 方法阻塞线程会使线程进入到 WAITING 状态。

public class Main {public static void main(String[] args) {Thread thread = new Thread(() -> {System.out.println("Thread is parked now");LockSupport.park();System.out.println("Thread is unparked now");});thread.start();try {Thread.sleep(3000); // 主线程等待3秒} catch (InterruptedException e) {e.printStackTrace();}LockSupport.unpark(thread); // 主线程唤醒阻塞的线程}
}

设计思路


LockSupport 会为使用它的线程关联一个许可证(permit)状态,permit 的语义「是否拥有许可」,0 代表否,1 代表是,默认是 0。

  • LockSupport.unpark:指定线程关联的 permit 直接更新为 1,如果更新前的permit<1,唤醒指定线程
  • LockSupport.park:当前线程关联的 permit 如果>0,直接把 permit 更新为 0,否则阻塞当前线程

  • 线程 A 执行LockSupport.park,发现 permit 为 0,未持有许可证,阻塞线程 A
  • 线程 B 执行LockSupport.unpark(入参线程 A),为 A 线程设置许可证,permit 更新为 1,唤醒线程 A
  • 线程 B 流程结束
  • 线程 A 被唤醒,发现 permit 为 1,消费许可证,permit 更新为 0
  • 线程 A 执行临界区
  • 线程 A 流程结束

经过上面的分析得出结论 unpark 的语义明确为「使线程持有许可证」,park 的语义明确为「消费线程持有的许可」,所以 unpark 与 park 的执行顺序没有强制要求,只要控制好使用的线程即可,unpark=>park执行流程如下:

  • permit 默认是 0,线程 A 执行 LockSupport.unpark,permit 更新为 1,线程 A 持有许可证
  • 线程 A 执行 LockSupport.park,此时 permit 是 1,消费许可证,permit 更新为 0
  • 执行临界区
  • 流程结束

因 park 阻塞的线程不仅仅会被 unpark 唤醒,还可能会被线程中断(Thread.interrupt)唤醒,而且不会抛出 InterruptedException 异常,所以建议在 park 后自行判断线程中断状态,来做对应的业务处理。

为什么推荐使用 LockSupport 来做线程的阻塞与唤醒(线程间协同工作),因为它具备如下优点:

  • 以线程为操作对象更符合阻塞线程的直观语义
  • 操作更精准,可以准确地唤醒某一个线程(notify 随机唤醒一个线程,notifyAll 唤醒所有等待的线程)
  • 无需竞争锁对象(以线程作为操作对象),不会因竞争锁对象产生死锁问题
  • unpark 与 park 没有严格的执行顺序,不会因执行顺序引起死锁问题,比如「Thread.suspend 和 Thread.resume」没按照严格顺序执行,就会产生死锁

面试题


有 3 个独立的线程,一个只会输出 A,一个只会输出 B,一个只会输出 C,在三个线程启动的情况下,请用合理的方式让他们按顺序打印 ABCABC。

public class Main {private static Thread t1, t2, t3;public static void main(String[] args) {t1 = new Thread(() -> {for (int i = 0; i < 2; i++) {LockSupport.park();System.out.print("A");LockSupport.unpark(t2);}});t2 = new Thread(() -> {for (int i = 0; i < 2; i++) {LockSupport.park();System.out.print("B");LockSupport.unpark(t3);}});t3 = new Thread(() -> {for (int i = 0; i < 2; i++) {LockSupport.park();System.out.print("C");LockSupport.unpark(t1);}});t1.start();t2.start();t3.start();// 主线程稍微等待一下,确保其他线程已经启动并且进入park状态。try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}// 启动整个流程LockSupport.unpark(t1);}
}

LockSupport 提供了一种更底层和灵活的线程调度方式。它不依赖于同步块或特定的锁对象。可以用于构建更复杂的同步结构,例如自定义锁或并发容器。LockSupport.park 与 LockSupport.unpark 的组合使得线程之间的精确控制变得更容易,而不需要复杂的同步逻辑和对象监视。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

计算机组成与体系结构-浮点数表示

定点数: 是一种在计算机中表示和处理实数的方法,其中,小数点的位置是固定的,不会随着数值的大小而变化。浮点数: 是计算机中用于表示实数的一种数据类型。小数点 位置浮动浮点数表示阶码(指数部分): 决定了浮点数可以表示的范围。阶码越长,可以表示的指数范围就越大 尾数(有…

渗透

渗透测试 一、简介 ​ 渗透测试(Penetration Testing)是一种通过模拟攻击的技术与方法,挫败目标系统的安全控制措施并获得控制访问权的安全测试方法。 ​ 网络渗透测试主要依据CVE(Common Vulnerabilities and Exposures,通用漏洞与披露)已经发现的安全漏洞,模拟入侵者的攻击…

学习Java的第四周

第四周的学习记录来喽,本周的重点就是之前提到过的判断和循环(其中包括流程控制语句的三种结构:顺序、分支、循环;顺序结构即Java程序的默认流程,分支结构学了if判断语句的三种格式、switch语句练习和扩展知识,循环结构学了for循环格式和练习、累加思想和统计思想、while…

idea在pom中引入第三方依赖

项目右侧点击maven,点击加号(鼠标放上去会出现add maven project字样) 找到要导入的项目的pom文件选中,ok(这里我以及导入过了,所以ok是灰色) 回到原项目中,在pom中添加dependency 其中,artifactId是导入进来的姓名的名字,groupId是导入进来的项目的java目录下的包名…

计算机组成与体系结构-cache

概念 Cache,高速缓冲存储器。是解决CPU与主存之间速度不匹配而采用的一项重要技术,位于主存与CPU之间,由静态存储芯片(SRAM)组成,容量比较小,Cache存储了频繁访问的内存数据命中率 CPU欲访问的信息已在Cache中的比率,设在一段程序执行期间cache完成存取次数为NC,主存完成…

《Programming from the Ground Up》阅读笔记:p88-p94

《Programming from the Ground Up》学习第5天,p88-p94总结,总计7页。 一、技术总结 1.touppercase.s #PURPOSE: This program converts an input file #to an output file with all letters #converted to uppercase.#PROCESSING: #(1)Open the input file #(2)Open the o…

计算机网络第三讲 数据链路层

计算机网络第三讲 数据链路层 第一节:数据链路层功能 1. 概述2. 数据链路层的功能封装成帧透明传输差错控制实现无比特差错 不是可靠传输 丢失,乱序,重复无法解决

计算机组成与体系结构-层次化存储体系

将数据分为多个层次进行存储,以最优的控制调度算法和合理的成本,构成具有性能可接受的存储系统. 解决两个矛盾采用层次化存储体系,可以通过平衡存储介质的速度和成本得到最佳的存储效用。解决了主存容量不足与高成本的矛盾、CPU与主存速度不匹配的矛盾存储器分类RAM,随机存…

计算机网络第一讲

计算机网络第一讲 计算机网络概述考点一 1. 计算机网络的定义2. 计算机网络的组成物理组成工作方式英特网中的服务 考研中只有 TCP是可靠服务,其他都是不可靠的 面向连接的服务只有1.TCP 2.PPP 其他所有均无连接有应答服务:1.TCP 2.CSMA/CA3. 计算机网络的特点 共享4. 计算机…

计算机组成原理基础第7讲 输入输出系统

计算机组成原理基础第7讲 输入输出系统2. 输入输出系统的组成考点二:I/O接口接口的功能和组成I/O接口的基本组成3. 接口类型4. 程序查询方式考点三:中断系统

ABC273F Hammer 题解

dp的本质是拓扑(),感觉可以套一个线段树优化建图搞到 nlogn,好喔ABC273F Hammer 题解 题目大意 数轴上有 \(n\) 个锤子和 \(n\) 堵墙,第 \(i\) 个锤子位于 \(x_i\),第 \(i\) 堵墙位于 \(y_i\),第 \(i\) 个锤子可以对应的敲开第 \(i\) 堵墙。以原点为起点,给定终点 \(t\…