【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.rhkb.cn/news/231368.html

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

相关文章

模板模式实现分布式锁实战

前言 分布式锁相信大家都有用过&#xff0c;常见的分布式锁实现方式例如redis、zookeeper、数据库都可以实现&#xff0c;而我们代码中强引用这些分布式锁的代码&#xff0c;那么当我们以后想替换分布式锁的实现方式时&#xff0c;需要修改代码的成本会很高&#xff0c;于是我…

ChatGPT怎么帮我上班的

1.解放生产力 1&#xff09;标准格式&#xff0c;完美输出。GPT对于公文等具有一定标准格式的文件&#xff0c;可以进行完美仿写&#xff0c;随随便便以假乱真那都是小菜一碟&#xff0c;这对于经常要开展规范成文的人来说&#xff0c;简直就是个福音&#xff0c;只要前期调教…

pyqt5用qtdesign设计页面时,去掉页面的空白界面、边框和标题栏

前言 Windows默认的标题栏有时候自己觉得不太美观&#xff0c;就想自己设计一个&#xff0c;然后把默认的去掉&#xff0c;并且把长方形的边框和多余的空表界面去掉&#xff0c;就是下图中圈出来的区域&#xff1a; 去掉之后的效果如图&#xff1a; 这样我们就可以自定义窗…

面试算法89:房屋偷盗

题目 输入一个数组表示某条街道上的一排房屋内财产的数量。如果这条街道上相邻的两幢房屋被盗就会自动触发报警系统。请计算小偷在这条街道上最多能偷取到多少财产。例如&#xff0c;街道上5幢房屋内的财产用数组[2&#xff0c;3&#xff0c;4&#xff0c;5&#xff0c;3]表示…

php ext-sodium 拓展安装 linux+windows

php编译安装(linux)&#xff0c;可以参考&#xff1a;php编译安装 一、windows soduim源码包自带&#xff0c;直接修改php.ini&#xff0c;取消extensionsodium注释即可 二、linux 1.安装依赖 apt-get install libsodium-dev2.进入源码目录 这里写自己的源码目录 cd /us…

大数据规模存储的几个核心问题

文章目录 三个关键问题RAID&#xff08;独立磁盘冗余阵列&#xff09;RAID是如何解决关于存储的三个关键问题&#xff1f;水平伸缩 大规模数据存储都需要解决几个核心问题&#xff0c;这些问题都是什么呢&#xff1f; 三个关键问题 1.数据存储容量的问题 既然大数据要解决的…

Windows系统中Wireshark抓包工具的安装使用

在使用Windows服务器时&#xff0c;如果我们发现网络流量异常或存在异常的外发数据包行为&#xff0c;我们可以利用抓包工具来捕获网络流量包&#xff0c;并对这些流量包进行特征分析&#xff0c;以查看其来源和目的地。通过这些信息&#xff0c;我们可以进一步诊断问题。 以下…

人生重开模拟器

前言&#xff1a; 人生重开模拟器是前段时间非常火的一个小游戏&#xff0c;接下来我们将一起学习使用c语言写一个简易版的人生重开模拟器。 网页版游戏&#xff1a; 人生重开模拟器 (ytecn.com) 1.实现一个简化版的人生重开模拟器 &#xff08;1&#xff09; 游戏开始的时…

如何快速定位php程序运行慢的地方

1 slow log日志 查看slowlog日志位置 编辑php-fpm.conf文件&#xff0c;更改或增加两行内容 slowlog /data/logs/php-slow.log request_slowlog_timeout 2 说明&#xff1a;slowlog定义日志路径和名字&#xff0c;request_slowlog_timeout定义超时时间&#xff0c;单位…

[足式机器人]Part2 Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-自动控制原理Ch1-6根轨迹Root locus 1. 根的作用2. 手绘技巧3. 分离点/汇合点&根轨迹的几何性质 1. 根的作用 G ( s ) s 3 s 2 2 s 4 G\left( s \right) \frac{s3}{s^22s4} G(s)s22s4s3​…

线性代数 --- 为什么LU分解中L矩阵的行列式一定等于(+-)1?

以下是关于下三角矩阵L的行列式一定等于-1的一些说明 证明&#xff1a;在LU分解中&#xff0c;下三角矩阵L的行列式一定是. 在证明之前&#xff0c;我这里先补充几条关于行列式的性质&#xff1a; 性质1&#xff1a;对于三角矩阵而言&#xff0c;不论是上三角矩阵还是下三角矩…

<六>Python的字符串切片及常见操作

字符串的表示 在Python里&#xff0c;可以使用一对单引号、一对双引号或者一对三个双引号、一对三个单引号表示字符串。 a "Im Tom" # 一对双引号 b Tom said:"I am Tom" # 一对单引号c Tom said:"I\m Tom" # 转义字符d Tom said:"…

C++矩阵例题分析(3):螺旋矩阵

一、审题 时间限制&#xff1a;1000ms 内存限制&#xff1a;256MB 各平台平均AC率&#xff1a;14.89% 题目描述 输出一个n*n大小的螺旋矩阵。 螺旋矩阵的样子&#xff1a; 输入描述 共一行&#xff0c;一个正整数n&#xff0c;表示矩阵变长的长度…

静态网页设计——电影角(HTML+CSS+JavaScript)

前言 声明&#xff1a;该文章只是做技术分享&#xff0c;若侵权请联系我删除。&#xff01;&#xff01; 使用技术&#xff1a;HTMLCSSJS 主要内容&#xff1a;本网页主要利用HTML语言编写&#xff0c;简要介绍世界上一些主要国家&#xff0c;例如&#xff0c;中&#xff0c;…

STM32通用定时器-输入捕获-脉冲计数

一、知识点 编码器   两相编码器&#xff08;正交编码器&#xff09;&#xff1a;两相编码器由 A 相和 B 相组成&#xff0c;相位差为 90 度。当旋转方向为顺时针时&#xff0c;A 相先变化&#xff0c;然后 B 相变化&#xff1b;当旋转方向为逆时针时&#xff0c;B 相先变化…

前端页面锚点跳转

一&#xff0c;页面 二&#xff0c;获取需要跳转的标签class或者id 三&#xff0c;调用跳转方法 如果你的标签有唯一的ID&#xff0c;那么用getElementById方法更好 点击即可跳转锚点

DDIA 第十一章:流处理

本文是《数据密集型应用系统设计》&#xff08;DDIA&#xff09;的读书笔记&#xff0c;一共十二章&#xff0c;我已经全部阅读并且整理完毕。 采用一问一答的形式&#xff0c;并且用列表形式整理了原文。 笔记的内容大概是原文的 1/5 ~ 1/3&#xff0c;所以你如果没有很多时间…

Python爬虫实战技巧:如何在爬取过程中动态切换代理IP

目录 前言 第一步&#xff1a;获取代理IP列表 第二步&#xff1a;测试代理IP的可用性 第三步&#xff1a;动态切换代理IP 总结 前言 在进行爬虫开发的过程中&#xff0c;有时候需要使用代理IP来访问目标网站&#xff0c;以避免被封IP或者降低访问频率的限制。本文将介绍如…

gem5学习(8):创建一个简单的缓存对象--Creating a simple cache object

目录 一、SimpleCache SimObject 二、Implementing the SimpleCache 1、getSlavePort() 2、handleRequest() 3、AccessEvent() 4、accessTiming() &#xff08;1&#xff09;缓存命中&#xff1a;sendResponse() &#xff08;2&#xff09;缓存未命中&#xff1a; 三、…

数据分析-25-电商用户行为可视化分析

文章目录 0. 数据代码获取1. 项目介绍1.1 分析背景1.2 分析目的1.3 分析思路 2. 数据清洗2.1 加载必要的库2.2 读取数据2.3 统计缺失值2.4 处理数据a. 删除重复值b. 转换时间格式c. 提取日期和时间d. 转换数据类型 3. 分析内容3.1 用户活跃规律a. 日均pv与uvb. 日新增pv、uv趋势…