【Java】LockSupport原理与使用

LockSupport:

关键字段

    private static final sun.misc.Unsafe UNSAFE;private static final long parkBlockerOffset;

        Unsafe:"魔法类",较为底层,在LockSupport类中用于线程调度(线程阻塞、线程恢复等)。

        parkBlockerOffset:锁对象的内存偏移量。

关键方法

        (1)park()

public static void park() {UNSAFE.park(false, 0L);}public native void park(boolean var1, long var2);

        可以看到LockSupport的park方法实现实际是由Unsafe类完成的。在Unsafe类中,park方法被native修饰,表示方法不是由Java语言实现,而是由C、C++提供,native关键字的作用是告诉编译器方法的实现将在程序运行时通过本地调用实现。

        第一个参数为false,代表线程相对当前时间阻塞;第二个参数为0L,代表阻塞的时间长度(第一个参数为true时才起作用,表示阻塞多久)。这两个参数共同作用表示当前线程挂起,直至有线程调用unpark(Thread t)方法,或者当前阻塞线程被其它线程打断。

        (2)unpark()

public static void unpark(Thread thread) {if (thread != null)UNSAFE.unpark(thread);}public native void unpark(Object var1);

        方法逻辑十分简单,对传入的Thread对象进行判空操作,随后通过Unsafe类解除挂起线程的阻塞状态。

        (3)park(Object blocker)

public static void park(Object blocker) {//获取当前线程Thread t = Thread.currentThread();//设置线程的锁对象setBlocker(t, blocker);//阻塞当前线程UNSAFE.park(false, 0L);//解除阻塞后,将锁对象置空setBlocker(t, null);}//设置线程的锁对象
private static void setBlocker(Thread t, Object arg) {// Even though volatile, hotspot doesn't need a write barrier here.// t:当前线程// parkBlockerOffset:锁对象的内存偏移量// arg:锁对象UNSAFE.putObject(t, parkBlockerOffset, arg);}//返回线程的锁对象
public static Object getBlocker(Thread t) {//参数为null,则抛出空指针异常if (t == null)throw new NullPointerException();//否则使用Unsafe类通过parkBlockerOffset内存偏移量找到锁对象并返回return UNSAFE.getObjectVolatile(t, parkBlockerOffset);}

        该方法的作用也是使当前线程阻塞,与无参的park相比多了一个锁对象:blocker,如果我们想让线程阻塞于某一对象,以便我们更好的了解和管理线程的阻塞状态就可以使用该方法。

        (4)parkUntil(long deadline)

public static void parkUntil(long deadline) {UNSAFE.park(true, deadline);}

       第一个参数为true代表绝对时间阻塞;第二个参数deadline由caller线程传递,代表截止时间。两个参数共同作用表示线程会一直阻塞,直至有线程调用unpark解除阻塞、当前阻塞线程被打断、到达deadline时间,三个条件有一个成立即可脱离阻塞状态。

使用

        LockSupport的使用与Semaphore有一定相似之处,不同点在于LockSupport的permit数量变更与Semaphore不同。

park方法

        每次调用park方法都会消耗一个permit。如果线程在调用park方法时已经存在一个permit,那么当前线程不会进入阻塞状态,而是消耗掉这个permit并立即返回;如果线程在调用park方法时没有permit(初始时,默认permit为0),那么当前线程会进入阻塞状态。图解如下:

unpark方法

        unpark方法与park()方法并不一一对应:当permit数量为0个时,线程调用unpark方法会使permit数量+1;当permit数量为1个时,再次调用unpark方法不会让permit继续累加,这意味着permit的最大值为1,最小值为0。图解如下:

        测试

package test;
import java.util.concurrent.locks.LockSupport;public class LockSupportTest {public static void main(String[] args) {Thread t = new Thread(() -> {try {//这里是为了让main线程先执行unpark动作Thread.sleep(150);} catch (InterruptedException e) {e.printStackTrace();}long start = System.currentTimeMillis();System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");LockSupport.park();long end = System.currentTimeMillis();System.out.println("经过:"+(end - start) +"ms后,脱离阻塞状态");},"Thread-1");t.start();System.out.println("main线程让permit数量从0变为1");LockSupport.unpark(t);}
}

        测试结果

        测试结果表明其它线程可以提前调用unpark方法,让permit数量变成1,当既定线程调用park方法时,由于permit数量不为0所以不会陷入阻塞,而是立即返回。

        让我们来看看permit能不能累加

package test;
import java.util.concurrent.locks.LockSupport;public class LockSupportTest {public static void main(String[] args) {Thread t = new Thread(() -> {try {//这里是为了让main线程先执行unpark动作Thread.sleep(150);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");LockSupport.park();System.out.println("调用park方法,permit数量为1则消耗掉一个permit,线程不阻塞;permit数量为0则当前线程进入阻塞状态");LockSupport.park();System.out.println("脱离阻塞状态");},"Thread-1");t.start();//mainSystem.out.println("main线程调用两次unpark方法");LockSupport.unpark(t);LockSupport.unpark(t);}
}

        测试结果

        我们可以看到,即使main线程调用了两次unpark方法,但Thread-1线程还是阻塞于第二次park方法调用,这直接证明:permit不能累加,permit的最大值为1。

        LockSupport相较于传统wait/notify实现线程同步的特点是简洁,整个过程没有锁对象的参与,让我们通过三线程交替打印A、B、C来感受一下LockSupoort的魅力:

package test;import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.LockSupport;//三线程交替打印A、B、C,一共打印4次
public class LockSupportTest {static AtomicInteger i = new AtomicInteger(0);static Thread[] threads = new Thread[3];public static void main(String[] args) {threads[0] = new Thread(() -> {while (i.get() < 12){System.out.print("A-" + i.getAndIncrement() + "   ");LockSupport.unpark(threads[1]);LockSupport.park();}},"Thread-1");threads[1] = new Thread(() -> {LockSupport.park();while (i.get() < 12){System.out.print("B-" + i.getAndIncrement() + "   ");LockSupport.unpark(threads[2]);if (i.get() > 10)break;LockSupport.park();}},"Thread-2");threads[2] = new Thread(() -> {LockSupport.park();while (i.get() < 12){System.out.println("C-" + i.getAndIncrement());LockSupport.unpark(threads[0]);if(i.get() > 11)break;LockSupport.park();}},"Thread-3");threads[0].start();threads[1].start();threads[2].start();}
}

        运行结果

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

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

相关文章

Windows安装部署nginx

1、官网下载安装包&#xff1a; 官网地址&#xff1a;https://nginx.org/en/download.html 下载好后&#xff0c;解压即可&#xff1a; 在nginx的配置文件是conf目录下的nginx.conf&#xff0c;默认配置的nginx监听的端口为80&#xff0c;如果本地80端口已经被使用则修改成其…

STM32F103C8T6制作简易示波器

1设计需求 通过stm32f103c8t6实现一个简易示波器功能&#xff0c;该示波器可以检测0-3.6khz频率范围内的波形。 也可以输出波形&#xff0c;输出方波、三角波、正弦波。 2技术方案 通过stm32的ADC功能&#xff0c;采集输入信号&#xff0c;最后由oled屏进行显示。 采样频率…

图像分割实战-系列教程10:U2NET显著性检测实战2

&#x1f341;&#x1f341;&#x1f341;图像分割实战-系列教程 总目录 有任何问题欢迎在下面留言 本篇文章的代码运行界面均在Pycharm中进行 本篇文章配套的代码资源已经上传 U2NET显著性检测实战1 U2NET显著性检测实战2 U2NET显著性检测实战3 5、残差Unet模块 class RSU7(n…

MySQL基础篇(四)事务

一、事务简介 事务是一组操作的集合&#xff0c;它是一个不可分割的工作单位&#xff0c;事务会把所有的操作作为一个整体一期向系统提交或撤销操作请求&#xff0c;即这些操作要么同时成功&#xff0c;要么同时失败。 注意&#xff1a; 默认 MySQL 的事务是 自动提交 的&#…

重磅发布|博睿数据2023年度精选案例集—— IT运维之光

当前&#xff0c;数字经济已经成为全球经济增长的重要引擎。随着新技术的飞速发展&#xff0c;企业数字化转型机遇不断涌现&#xff0c;而稳定、安全、可靠的IT运维环境是实现数字化转型的关键。 在此背景下&#xff0c;AIOps 智能运维正成为企业高效管控种类繁多数量庞大的物…

CommonJS 和 ES6 Module:一场模块规范的对决(下)

&#x1f90d; 前端开发工程师&#xff08;主业&#xff09;、技术博主&#xff08;副业&#xff09;、已过CET6 &#x1f368; 阿珊和她的猫_CSDN个人主页 &#x1f560; 牛客高级专题作者、在牛客打造高质量专栏《前端面试必备》 &#x1f35a; 蓝桥云课签约作者、已在蓝桥云…

【Python学习】2024PyCharm插件推荐

目录 【Python学习】2024PyCharm插件推荐 1. Key Promoter X2.Rainbow CSV3.Markdown4.Rainbow Brackets5.Indent Rainbow6.Regex Tester7.Regex Tester8.Background Image Plus9.Material Theme UI10. Chinese 汉化插件参考 文章所属专区 Python学习 1. Key Promoter X 方便…

frp配置内网穿透访问家里的nas

frp配置内网穿透访问家里的nas 需求 家里局域网内有台nas&#xff0c;在去公司的路上想访问它 其内网地址为&#xff1a; http://192.168.50.8:6002 工具 1.frp版本v0.53.2 下载地址&#xff1a; https://github.com/fatedier/frp/releases/download/v0.53.2/frp_0.53.2_li…

十、基本对话框大集合(Qt5 GUI系列)

目录 一、设计需求 二、实现代码 三、代码解析 四、总结 一、设计需求 Qt提供了很多标准的对话框。例如标准文件对话框(QFileDialog)、标准颜色对话框(QColorDialog)、标准字体对话框 (QFontDialog)、标准输入对话框 (QInputDialog) 及消息对话框 (QMessageBox)。本文展示各…

1.4补码范围,溢出,补码加减法、加法器、竞争与冒险、杂项

正数三码合一 负数的原码有1的符号位&#xff0c;反码为除了符号位以外全部取反&#xff0c;补码在反码的基础上再加1 移码的符号位中0表示负数&#xff0c;1表示正数&#xff0c;简单来说&#xff0c;原码的补码数值位不变&#xff0c;符号位取反就是移码。 对于8位寄存器: …

【docker】一文讲完docker搭建私有仓库

一、docker搭建私有仓库方法总结 搭建Docker私有仓库主要有以下几种方式&#xff1a; 使用Docker官方提供的Registry镜像&#xff1a;Docker官方提供了一个用于构建私有镜像仓库的Registry镜像&#xff0c;只需将镜像下载并运行容器&#xff0c;然后暴露5000端口即可使用。可以…

Go语言中的HTTP路由处理

在Web开发中&#xff0c;路由处理是至关重要的部分。它决定了当用户访问某个URL时&#xff0c;服务器应该如何响应。Go语言提供了多种库和工具来处理HTTP路由。下面&#xff0c;我们将深入了解如何在Go语言中处理HTTP路由。 Go语言的net/http包本身提供了基本的功能来处理路由…

大数据毕业设计:python房源数据爬虫分析预测系统+可视化 +商品房数据(源码+讲解视频)✅

毕业设计&#xff1a;2023-2024年计算机专业毕业设计选题汇总&#xff08;建议收藏&#xff09; 毕业设计&#xff1a;2023-2024年最新最全计算机专业毕设选题推荐汇总 &#x1f345;感兴趣的可以先收藏起来&#xff0c;点赞、关注不迷路&#xff0c;大家在毕设选题&#xff…

【Spring 篇】Spring:轻松驾驭 Java 世界的利器

在 Java 开发领域&#xff0c;Spring 框架无疑是一颗璀璨的明星&#xff0c;它不仅提供了全面的企业级特性&#xff0c;还为开发者提供了简便而强大的开发方式。本文将深入探讨 Spring 框架的简介、配置和快速入门&#xff0c;带你轻松驾驭 Java 世界的利器。 Spring 简介 Sp…

YOLOv8改进:IoU系列篇 | Shape-IoU结合基于辅助边框的Inner-IoU损失,实现再次创新

🚀🚀🚀本文改进: Shape-IoU结合基于辅助边框的Inner-IoU损失,小目标检测实现涨点,基于辅助边框的优化前提下,更加关注边界框本身的形状和尺度来计算损失 🚀🚀🚀YOLOv8改进专栏:http://t.csdnimg.cn/hGhVK 学姐带你学习YOLOv8,从入门到创新,轻轻松松搞定科研…

Maven(mvn)的学习下载和配置

文章目录 Maven&#xff08;mvn&#xff09;1.Maven 是什么&#xff1f;2.Maven做什么&#xff1f;2.1传统方式对项目的管理2.2Maven对jar包的管理 3.Maven怎么学3.1Maven如何创建项目3.2Maven的下载与配置3.3Maven的项目结构3.4Maven依赖的引入3.5Maven依赖的剔除3.6Maven依赖…

1050. 鸣人的影分身(dp划分)

题目&#xff1a; 1050. 鸣人的影分身 - AcWing题库 输入样例&#xff1a; 1 7 3输出样例&#xff1a; 8 思路&#xff1a; 代码&#xff1a; #include<iostream> using namespace std; const int N20; int f[N][N]; int main() {int T,m,n;cin>>T;while(T--)…

小红书玩法策略汇总,品牌运营攻略

众所周知&#xff0c;小红书平台凭借其出色的品宣传能力和产品种草能力&#xff0c;而稳坐内容平台C位。那么不论是作为达人&#xff0c;还是品牌方&#xff0c;进驻小红书平台后该如何进行传播&#xff0c;获取流量呢?我们今天和大家分享下小红书玩法策略汇总&#xff0c;品牌…

python练习3【题解///考点列出///错题改正】

一、单选题 1.【单选题】 ——可迭代对象 下列哪个选项是可迭代对象&#xff08; D&#xff09;&#xff1f; A.(1,2,3,4,5) B.[2,3,4,5,6] C.{a:3,b:5} D.以上全部 知识点补充——【可迭代对象】 可迭代对象&#xff08;iterable&#xff09;是指可以通过迭代&#xff…

专业课130+,总分390+四川大学951信号与系统考研通信,电子信息经验分享

今年专业课130&#xff0c;总分390&#xff0c;顺利上岸&#xff0c;将近一年复习一路走来&#xff0c;感慨很多&#xff0c;希望以下经历可以给后来的同学提供一些参考。 初试备考经验 公共课&#xff1a;三门公共课&#xff0c;政治&#xff0c;英语&#xff0c;数学。在备…