AQS阻塞唤醒工具LockSupport

LockSupport在JDK源码中描述为:构建锁和其他同步类的基本线程阻塞原语,构建更高级别的同步工具集。LockSupport提供的park/unpark从线程的粒度上进行阻塞和唤醒,park/unpark模型真正解耦了线程之间的同步,线程之间不再需要一个Object或者其它变量来存储状态。
本文从阻塞唤醒的语义入手,解释LockSupport的内在机制和注意点,最后与Object的wait和notify做对比,包括以下内容:
- 阻塞和唤醒的语义
- 许可机制
- 底层实现
- 用法
- 与Object的wait和notify区别
阻塞的语义
阻塞是线程在满足某种条件之前暂时停止运行,而被动释放出CPU资源。进入该状态的线程不会主动进入线程队列等待CPU资源,而需要等待满足条件后被唤醒,才能让该线程重新进入到线程队列中排队等待CPU资源。一般造成线程阻塞的原因有:等待获取一个已经被其他线程持有的排他锁、等待某一操作结束、等待某一个时间段。线程阻塞后会被挂起,此时会处于BLOCKED、WAITING、TIMED_WAITING。线程阻塞后会让出CPU资源,这是为避免在自旋上浪费过多的CPU资源,是忙等待(busy wait)的一种优化。
许可机制
LockSupport通过“许可”(permit)机制,使用park/unpark实现线程的阻塞和唤醒。许可是指允许线程继续执行,是线程执行的开关,当开关关闭时,线程会阻塞,当开关打开时,线程会立即执行。
park意指线程在获取许可之前会暂停执行(阻塞在获取许可)。这里的“许可”与线程相关联,类似二元信号量,不可叠加且一个线程只能有一个。有些文章描述“许可”是一次性的,例如当线程A调用park消耗掉一个“许可”(最多只有一个“许可”),在未调用unpark释放出线程A的该“许可”之前,线程A再次调用park时会阻塞在获取“许可”。下文引自Understanding JVM Thread States
there can be only one permit per thread, when thread consumes the permit, it disappears.
出于线程调度的目的,调用park时会阻塞直到许可可用时。如果许可可用,调用park就会立即返回。当前线程就会阻塞,直到调用 unpark 方法,释放出许可。由于许可是默认被占用的,当前线程在启动后调用 park 的话就获取不到许可,因此就进入阻塞状态。
底层实现
LockSupport是使用Unsafe的park实现的,HotSpot Parker用condition和mutex维护了一个_counter变量,park时,变量_counter置为0,unpark时,变量_counter置为1。park操作检查该值是否为1,为1直接返回;不为1,则阻塞。
用法
看到一个关于park/unpark通俗易懂的的例子,代码如下:
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("周末了我在打游戏");
LockSupport.park();
System.out.println("陪女朋友逛逛街");
}
});
threadA.start();
Thread.sleep(3000);
System.out.println("女朋友准备要喊男朋友逛街");
LockSupport.unpark(threadA);
}
|
在第6行park执行操作时,线程尝试获取许可,由于线程threadA在启动后默认已经获取了许可,park必须等待许可释放后才可以执行。当主线程调用unpark方法释放threadA的许可,threadA才可以继续执行第7行。
与wait和notify区别
park/unpark与wati/notify都提供阻塞唤醒的功能,用做线程间同步,不过两者 的粒度不同,park/unpark作用在线程上,而wait/notify作用在对象上,二者没有交集。Object的wait/notify使用前必须获取对象的监视器,而park/unpark不需要。
写作不易,痛并快乐着;理解可能存在偏差,句句斟酌推敲;抵制抄袭,践行原创技术之路。如果本文能对您有所帮助,实为荣幸,我是葛一凡。
原文
微信公众号
参考
AQS阻塞唤醒工具LockSupport的更多相关文章
- 阻塞和唤醒线程——LockSupport功能简介及原理浅析
目录 1.LockSupport功能简介 1.1 使用wait,notify阻塞唤醒线程 1.2 使用LockSupport阻塞唤醒线程 2. LockSupport的其他特色 2.1 可以先唤醒线程 ...
- java线程阻塞唤醒的四种方式
java在多线程情况下,经常会使用到线程的阻塞与唤醒,这里就为大家简单介绍一下以下几种阻塞/唤醒方式与区别,不做详细的介绍与代码分析 suspend与resume Java废弃 suspend() 去 ...
- Java并发框架——AQS阻塞队列管理(三)——CLH锁改造
在CLH锁核心思想的影响下,Java并发包的基础框架AQS以CLH锁作为基础而设计,其中主要是考虑到CLH锁更容易实现取消与超时功能.比起原来的CLH锁已经做了很大的改造,主要从两方面进行了改造:节点 ...
- 看看AQS阻塞队列和条件队列
上一篇简单介绍了AQS,我们大概知道AQS就是一个框架,把很多功能都给实现了(比如入队规则,唤醒节点中的线程等),我们如果要使用的话只需要实现其中的一些方法(比如tryAcquire等)就行了!这次主 ...
- 多线程同步工具——LockSupport
用例1:子线程等待主线程发放许可! public static void main(String[] args) { Thread thread = new Thread(){ public void ...
- 如何实现Java线程的 阻塞/唤醒(和暂停/继续 类似)
以下为线程 阻塞/唤醒 主要代码 public class MyThread extends Thread { //无意义 private final Object lock = new Objec ...
- 【Java并发编程实战】----- AQS(三):阻塞、唤醒:LockSupport
在上篇博客([Java并发编程实战]----- AQS(二):获取锁.释放锁)中提到,当一个线程加入到CLH队列中时,如果不是头节点是需要判断该节点是否需要挂起:在释放锁后,需要唤醒该线程的继任节点 ...
- Java并发包源码学习系列:挂起与唤醒线程LockSupport工具类
目录 LockSupport概述 park与unpark相关方法 中断演示 blocker的作用 测试无blocker 测试带blocker JDK提供的demo 总结 参考阅读 系列传送门: Jav ...
- 一文带你学会AQS和并发工具类的关系
1. 存在的意义 AQS(AbstractQueuedSynchronizer)是JAVA中众多锁以及并发工具的基础,其底层采用乐观锁,大量使用了CAS操作, 并且在冲突时,采用自旋方式重试,以实 ...
随机推荐
- [笔记]A Practical Guide to Support Vector Classication
<A Practical Guide to Support Vector Classication>是一篇libSVM使用入门教程以及一些实用技巧. 1. Basic Kernels: ( ...
- ssh配置文件及问题解决
一 ssh的配置文鉴模板 Host AAA User gitolite3 HostName IP地址 IdentityFile ~/.ssh/key 二 下载代码的方法 1 ssh git2 git ...
- shell 脚本浅入
最常用的Linux命令和工具 目录下个文档:cd name 返回上个目录:cd .. 查看.编辑文本文件:查看文件:more, tail 编辑文件:vi 如编写脚本.sh vi shell.sh ...
- jq-实战之表格筛选
css部分 .select{background: #ccc} html部分 table width="> <thead> <th>姓名</th> ...
- Canvas + JavaScript 制作图片粒子效果
首先看一下源图和转换成粒子效果的对比图: 左侧图片为源图,右侧图片为粒子效果图.该效果是在Canvas画布上制作的.将图片制作成粒子效果相对而言是比较简单的.重点了解两个知识点即可 1:图 ...
- DB2函数大全
DB2函数大全 函数名 函数解释 函数举例 AVG() 返回一组数值的平均值. SELECTAVG(SALARY)FROMBSEMPMS; CORR(),CORRELATION() 返回一对数值的关系 ...
- PHP反射之类的反射
最近在琢磨如何用PHP实现站点的插件功能,需要用到反射,于是现学了一下,笔记如下: class Person { public $name = 'Lily'; public $gender = 'ma ...
- redis安装-单机版
环境准备 因为redis使用c语言开发的,如果要运行在linux上,需要gcc-c++的环境.那么我们就要先看一下是否安装了gcc-c++如果没有的话,需要在虚拟机上安装gcc-c++环境(友情提示, ...
- 谁该吃药了(线性判别法LDA小故事)
一家"胡说八道医院"拥有一种治疗癌症的药物, 根据过去的记录, 该药物对一些患者非常有效, 但是会让一些患者感到更痛苦... 我们希望有一种判别准则能帮助我们判断哪些病人该吃药,哪 ...
- Python学习--20 Web开发
HTTP格式 HTTP协议是基于TCP和IP协议的.HTTP协议是一种文本协议. 每个HTTP请求和响应都遵循相同的格式,一个HTTP包含Header和Body两部分,其中Body是可选的. HTTP ...