java LockSupport实现原理示例解析
作者:小海编码日记 发布时间:2023-05-14 06:43:05
引言
前文中了解到AQS借助LockSupport.park和LockSupport.unpark完成线程的阻塞和唤醒,那么LockSupport内部又是怎么实现的?这是一个什么类?
LockSupport是用于使用锁阻塞线程的基础实现,是其他同步类的基础,这个类为每个使用它的线程关联一个许可证(有点类似于Semaphore),如果许可证可用,线程调用park方法时会立即返回,线程正常执行,否则当前线程阻塞,直到有其他线程调用unpark使得许可证可用,此时线程被唤醒,再次尝试获取许可证,其内部定义的park和unpark方法提供了阻塞和解决阻塞的基本实现。
LockSupport常见函数
如下表所示:
函数名称 | 说明 | 备注 |
---|---|---|
void park() | 阻塞当前线程 | 在下列情况发生时唤醒: 1.调用unpark函数,释放该线程的许可; 2.该线程被中断; 3.设置的阻塞超时时间耗尽; 4.到达设置的指定时间 |
void park(Object blocker) | 使用指定的blocker对象阻塞当前线程 | 唤醒条件,park中已说明 |
void parkNanos(long nanos) | 阻塞当前线程直到超时时间耗尽,nanos为指定的超时时间 | 唤醒条件,park中已说明 |
void parkNanos(Object blocker, long nanos) | 在超时时间耗尽前,使用指定的blocker对象阻塞当前线程,如果在到达超时时间后,许可仍不可用,则结束阻塞 | 唤醒条件,park中已说明 |
void parkUntil(long deadline) | 在指定时间前,阻塞该线程,如果在到达指定时间后,许可仍不可用,则结束阻塞 | 唤醒条件,park中已说明 |
void parkUntil(Object blocker, long deadline) | 在指定时间前,使用指定的对象阻塞该线程,如果在到达指定时间后,许可仍不可用,则结束阻塞 | 唤醒条件,park中已说明 |
void unpark(Thread thread) | 用于唤醒传入的在阻塞中的线程 | / |
Object getBlocker(Thread t) | 获取当前线程的阻塞对象 | / |
void setBlocker(Thread t, Object arg) | 使用指定对象为线程设置阻塞对象 | / |
LockSupport.park
ReentrantLock中调用LockSupport.park代码如下所示:
// AbstractQueuedSynchronizer.java
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this);
return Thread.interrupted();
}
在LockSupport中,void park(Object blocker)
实现代码如下:
// LockSupport.java
public static void park(Object blocker) {
Thread t = Thread.currentThread();
setBlocker(t, blocker);
UNSAFE.park(false, 0L);
setBlocker(t, null);
}
可以看到在park流程中主要包含以下过程:
获取当前线程
将传入的对象设置为该线程的parkBlocker
setBlocker函数实现如下所示:
private static void setBlocker(Thread t, Object arg) {
// Even though volatile, hotspot doesn't need a write barrier here.
UNSAFE.putObject(t, parkBlockerOffset, arg);
}
可以看到这里新出现了UNSAFE和parkBlockerOffset两个标识,这两个是用来干嘛的?我们一起看看其声明的代码:
private static final sun.misc.Unsafe UNSAFE;
private static final long parkBlockerOffset;
static {
try {
UNSAFE = sun.misc.Unsafe.getUnsafe();
Class<?> tk = Thread.class;
parkBlockerOffset = UNSAFE.objectFieldOffset
(tk.getDeclaredField("parkBlocker"));
.....
} catch (Exception ex) { throw new Error(ex); }
}
可以看到UNSAFE对象是通过Unsafe.getUnsafe()获取的,那么Unsafe这个类到底是干嘛的?
大家都知道Java对象在内存中创建,大多数情况下我们都是通过类的对象去修改和访问内存中的数据的,那么如果需要直接从内存修改某一对象的取值,应该怎么做呢?就是使用Unsafe类,该类只允许在JDK信任的类中调用(当前也可以用反射实例化该类对象)。
在Unsafe类中定义了两个重要函数park和unpark,其中park用于实现线程阻塞,unpark用于实现线程唤醒(Unsafe本质上是操作线程的Parker对象来完成线程阻塞和唤醒的,具体见参考链接,了解即可),上文中的parkBlockerOffset正是定义了Thread类的parkBlocker属性成员的内存偏移量,使用该值再结合Unsafe对象就可以实现直接操作内存中的parkBlocker值的目的,Thread类中的parkBlocker声明如下:
// Thread.java
volatile Object parkBlocker;
可以得到这一环节主要是将AQS作为blocker设置到当前线程的parkBlocker成员属性上。
CAS底层也是通过Unsafe执行的
执行UNSAFE.park
结合上文可知,这步完成后,当前线程阻塞
设置线程的parkBlocker为null
第三步中线程处于阻塞状态,当然就不能执行设置parkBlocker为null的操作了,那么什么时候执行呢?当线程从阻塞状态唤醒时,执行该步骤,使得线程的parkBlocker对象恢复初始状态。
LockSupport.unpark
LockSupport.unpark代码如下所示:
// LockSupport.java
public static void unpark(Thread thread) {
if (thread != null)
UNSAFE.unpark(thread);
}
可以看出当传入的线程不为空时,执行Unsafe的park函数唤醒当前线程,取消阻塞,此时继续执行park函数中的setBlocker(null),将parkBlocker成员设置为null。
参考链接 https://www.jb51.net/article/272073.htm
来源:https://juejin.cn/post/7186296167406731321


猜你喜欢
- SpringCloud @FeignClient 参数详解今天因为工作中遇到FeignClient一个奇葩的bug,后面仔细研究了,找出了原
- HTTPclient保持长连接首先解释一下什么是长连接当我们向一台服务器发起请求时,我们需要和对方建立一条通道,去传输数据,所谓的短连接,就
- 本文实例讲述了C#使用xsd文件验证XML格式是否正确的实现方法。分享给大家供大家参考,具体如下://创建xmlDocumentXmlDoc
- 上一篇中说到了 Expression 的一些概念性东西,其实也是为了这一篇做知识准备。为了实现 EFCore 的多条件、连表查询,简化查询代
- 一、简介Mutex的突出特点是可以跨应用程序域边界对资源进行独占访问,即可以用于同步不同进程中的线程,这种功能当然这是以牺牲更多的系统资源为
- 本文详述了android抽奖程序的实现方法,程序为一个抽奖大转盘代码,里面定义了很多图形方法和动画。实现主要功能的SlyderView.ja
- sqlite是啥?1、一种轻型数据库2、关系型数据库3、占用资源很低,几百K内存,适合嵌入式设备4、支持windows、linux、unix
- 发展历史Gradle 的依赖管理是一个从开始接触 Android 开发就一直伴随着我们的问题(作者是Andro
- 一、介绍knife4j增强版本的Swagger 前端UI,取名knife4j是希望她能像一把匕首一样小巧,轻量,并且功能强悍,更名也是希望把
- 读取resources下文件的方法网上有问答如下:问:new FileInputStream("src/main/resource
- 本文实例讲述了Android使用shape使组件呈现出特殊效果的方法。分享给大家供大家参考,具体如下:使用到的布局文件<?xml ve
- C#的System.Collections命名空间包含可使用的集合类和相关的接口,提供了集合的基本功能。包括了.NET下的非泛型集合类以及非
- 前言WebView(网络视图)在Andorid中就是用来显示网页的,下面我们来一起看看它是如何使用的。一、基本使用1.声明权限,WebVie
- 这篇文章主要介绍了Java方法重载Overload原理及使用解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值
- 对话框(Dialog)是Android系统在Activity或者其他组件运行过程中提供的一种提示机制。它可以帮助应用完成一些必要的提示功能,
- Spring发布了一个新工具Spring Native Beta,用于将现有的Spring Boot应用程序(用Java或Kotlin编写)
- 如下所示:@Overridepublic void onWindowFocusChanged(boolean hasFocus) {supe
- 前言作为一个写java的使用最多的轻量级框架莫过于spring,不管是老项目用到的springmvc,还是现在流行的springboot,都
- 在上一篇《Android仿微信滑动弹出编辑、删除菜单效果、增加下拉刷新功能》里,已经带着大家学习如何使用SwipeMenuListView这
- 前言C#中Try-Catch语句大家都很熟悉了,但是细究起来,还是有很多东西可讲的。最近在翻看之前总结的常见面试题中,发现关于try...c