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操作, 并且在冲突时,采用自旋方式重试,以实 ...
随机推荐
- xshell无法连接到linux主机原因分析
xshell连接linux主机时,会出现错误:Could not connect to '192.168.89.144' (port 22): Connection failed. 但是这时能pin ...
- 深圳尚学堂:JavaScript的常见面试题
经常说编程是一门技术专业,在找工作时面试官肯定不止看你的面试能力,还要看你的专业知识掌握,他可能会让你做一个小的编程测试,或者说考察你的语法知识掌握,今天,总结了一些关于在JavaScript中常常会 ...
- Java中boolean类型到底占用多少字节
虽然 Java 虚拟机定义了 boolean 这种数据类型,但是只对它提供了非常有限的支持.在 Java 虚拟机中没有任何供 boolean 值专用的字节码指令,在 Java 语言之中涉及到 bool ...
- C++编程练习(5)----“实现简单的循环队列的顺序存储结构“
队列(queue)是只允许在一端进行插入操作,而在另一端进行删除操作的线性表. 队列是一种先进先出(First In First Out)的线性表,简称FIFO.允许插入的一端称为队尾,允许删除的一端 ...
- java_JDBC(2)
1.Statement 每次执行sql语句,数据库都要执行sql语句的编译 ,最好用于仅执行一次查询并返回结果的情形,效率高于PreparedStatement. 2.PreparedStatemen ...
- Iterator——迭代接口
迭代对于JAVA的来说绝对不陌生.我们常常使用JDK提供的迭代接口进行Java集合的迭代. Iterator iterator = list.iterator(); while(iterator.ha ...
- 安卓UDP通信2
服务器实现一发一收 服务器代码: import java.net.*; import java.io.*; public class udpRecv2 { /* * 创建UDP传输的接收端 * 1.建 ...
- [Linux] PHP程序员玩转Linux系列-lnmp环境的搭建
1.PHP程序员玩转Linux系列-怎么安装使用CentOS 在平常的工作中,我作为PHP程序员经常要搭建一下环境,这个环境就是Linux系统下安装nginx,php,mysql这三个软件,对软件进行 ...
- node-webkit制作桌面应用
心血来潮突然想用js尝试写桌面应用,突然发现我大js真的无所不能.在网上搜到了这么一个东东:node-webkit.用Node.js来进行系统资源的访问,用HTML+CSS完成页面的搭建.哇,一切突然 ...
- java8-lamba表达式的使用-遁地龙卷风
(-1)前言 学习lamba表达式是十分重要的,你会发现java变的可爱多了. (0)函数式接口 只有一个方法的接口称为函数式接口 JButton jButton = new JButton(&quo ...