Java并发包的灵魂AbstractQueuedSynchronizer
JUC系列文章目录
JUC系列往期文章
文章目录
- JUC系列文章目录
- 前言
- 一、AQS的三大核心
- 1、state
- 2、控制线程抢锁和配合的FIFO队列
- 3、获取和释放方法
- 二、从ReentrantLock的实现说AQS
前言
我们在写这个系列的时候提到了很多并发工具,比如ReentrantLock、Semaphore,在这些工具的实现里面都用了一个共同的基类,也就是这篇文章要介绍的AbstractQueuedSynchronizer(AQS)。AQS是一个用于构建锁、同步器、协作工具类的框架,提供了同步状态的原子管理、阻塞和唤醒线程以及队列的管理功能。
一、AQS的三大核心
1、state
state是AQS内部一个用volatile修饰的成员变量,关于它的含义由具体实现AQS的类有关,在ReentrantLock中,state代表重入的次数,state为0代表没有线程持有锁;而在Semaphore中代表有限资源的剩余数量。为了保证state是线程安全的,每次修改依赖于atomic包下的cas操作。
2、控制线程抢锁和配合的FIFO队列
当多个线程争用同一把锁时,必须有排队机制将那些没能拿到锁的线程串在一起。当锁释放时,锁管理器就会挑选一个合适的线程来占有这个刚刚释放的锁。AQS会维护这样一个等待的线程队列,其结构是双向列表结构。
在AQS内部含有两条 Queue(Sync Queue, Condition Queue),其中Sync Queue是一个双向链表,里面储存的是处于等待状态的线程,正在排队等待唤醒去获取锁,而Condition Queue是一个单向链表,里面储存的也是处于等待状态的线程,只不过这些线程唤醒的结果是加入到了Sync Queue的队尾, AQS 所做的就是管理这两个队列里面线程之间的等待状态-唤醒的工作。
AQS 是一个抽象类,所以不能直接实例化,当我们需要实现一个自定义锁的时候可以去继承 AQS 然后重写获取锁的方式和释放锁的方式还有管理state,而 ReentrantLock 就是通过重写了 AQS 的
tryAcquire 和 tryRelease 方法实现的 lock 和 unlock 。
具体的Condition Queue源码分析可以看这篇文章《一行一行源码分析清楚 AbstractQueuedSynchronizer (二)》
3、获取和释放方法
具体的方法由其实现类实现,获取方法依赖于state,获取不到锁就会阻塞,等待其它线程释放锁之后再重新尝试加锁,那线程是如何实现阻塞自己的?其它线程释放锁之后又是如果唤醒当前线程的?当前线程是如何得出自己没有加锁成功这一结论的?具体答案可以看这篇文章《打通 Java 任督二脉 —— 并发数据结构的基石》
二、从ReentrantLock的实现说AQS
ReentrantLock 默认采用非公平锁,公平锁的 lock 方法如下,Sync是ReentrantLock的一个内部类,其实现继承了AQS,实现了相关方法。每一步代码的具体含义可以看这篇文章《一行一行源码分析清楚AbstractQueuedSynchronizer》的分析
static final class FairSync extends Sync {final void lock() {acquire(1);}// AbstractQueuedSynchronizer.acquire(int arg)public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 1. 和非公平锁相比,这里多了一个判断:是否有线程在等待if (!hasQueuedPredecessors() &&compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0)throw new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;}
}
非公平锁的 lock 方法如下,非公平锁在调用 lock 后,首先就会调用 CAS 进行一次抢锁,如果这个时候恰巧锁没有被占用,那么直接就获取到锁返回了。非公平锁在 CAS 失败后,和公平锁一样都会进入到 tryAcquire 方法,在 tryAcquire 方法中,如果发现锁这个时候被释放了(state == 0),非公平锁会直接 CAS 抢锁,但是公平锁会判断等待队列是否有线程处于等待状态,如果有则不去抢锁,乖乖排到后面。
如果这两次 CAS 都不成功,那么后面非公平锁和公平锁是一样的,都要进入到阻塞队列等待唤醒。
static final class NonfairSync extends Sync {final void lock() {// 2. 和公平锁相比,这里会直接先进行一次CAS,成功就返回了if (compareAndSetState(0, 1))setExclusiveOwnerThread(Thread.currentThread());elseacquire(1);}// AbstractQueuedSynchronizer.acquire(int arg)public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}protected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}
}
/*** Performs non-fair tryLock. tryAcquire is implemented in* subclasses, but both need nonfair try for trylock method.*/
final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {// 这里没有对阻塞队列进行判断if (compareAndSetState(0, acquires)) {setExclusiveOwnerThread(current);return true;}}else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // overflowthrow new Error("Maximum lock count exceeded");setState(nextc);return true;}return false;
}
Java并发包的灵魂AbstractQueuedSynchronizer相关推荐
- 深入java并发包源码(三)AQS独占方法源码分析
深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 AQS 的实现原理 学完用 AQS 自定义一个锁以后,我们可以来看 ...
- Java并发包基石-AQS详解
目录 1 基本实现原理 1.1 如何使用 1.2 设计思想 2 自定义同步器 2.1 同步器代码实现 2.2 同步器代码测试 3 源码分析 3.1 Node结点 3.2 独占式 3.3 共享式 4 总 ...
- Java并发包源码学习之AQS框架(三)LockSupport和interrupt
接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...
- java并发包详解(jdk7)
在此对java并发包做一个大致总结,如有错误,请指正. juc包的总体结构大致如下 外层框架主要有Lock(ReentrantLock.ReadWriteLock等).同步器(semaphores等) ...
- Java并发包源码学习系列:同步组件CountDownLatch源码解析
文章目录 CountDownLatch概述 使用案例与基本思路 类图与基本结构 void await() boolean await(long timeout, TimeUnit unit) void ...
- juc是个什么鬼(一) Java并发包详情,CAS分析,解决ABA问题
JUC就是java.util.concurrent包,俗称java并发包 通过看JDK的API,我们发现JUC下有俩子包,分别是atomic和locks包,这篇文章重点就是看这两个包下的内容 Atom ...
- 腾讯面试题Java 并发包之线程池综述
Java 并发包之线程池综述 ■ 线程池的创建 在Java中,您可以通过调整-Xss参数来调节每个线程栈的大小(64bit系统默认1024KB),当减小该值时意味着可以创建更多的线程数,但问题是JVM ...
- 死磕java并发cas_死磕 java并发包之AtomicInteger源码分析
问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicIn ...
- java并发包系列---LockSupport
长久以来对线程阻塞与唤醒经常我们会使用object的wait和notify,除了这种方式,java并发包还提供了另外一种方式对线程进行挂起和恢复,它就是并发包子包locks提供的LockSupport ...
最新文章
- 连接网络计算机后用户名更改不,电脑修改ip后无法上网
- [转]ASP.NET 缓存(十六)--检索缓存项的值
- VMware View 与Citrix XenDesktop对决之用户体验篇
- python 编译exe
- linux下面子目录绑定域名的方法,.htaccess绑定子域名到子目录方法
- Silverlight MMORPG团队项目截图
- JavaScript常用内置对象之Array
- 透明的WinForm窗体
- 微信支付PKIX path building failed
- JPA EntityManagers,事务及其周围的一切
- jsp oracle连接池,利用Oracle自带的连接池类的一例
- [Java] 蓝桥杯ALGO-139 算法训练 s01串
- c语言数组元素前移后移,如何将一个数组的元素循环左移?
- 免费证件照制作的软件有哪些?来看看这几个好用的软件
- 在线教育平台项目——设计 接口定义
- 为你的TabBar添加Badge-感谢分享
- 自动驾驶——Smooth Local Planning
- C#,动态规划问题中基于单词搜索树(Trie Tree)的单词断句分词( Word Breaker)算法与源代码
- C语言手机通讯录系统
- 算法:数字的排列组合问题
热门文章
- java保留两位小数并向上取整_Java取整,固定保留两位小数,适配负数、金融数字。...
- BZOJ 3309: DZY Loves Math
- 面试宝典 | 不完全总结
- 你坚持的哪些细小的好习惯
- 适用于通达信软件,股票两高点划线的代码
- Matplotlib 绘制 双轴 图
- 批量的将excel转换成pdf格式的方法
- Redis关闭持久化
- Help and Manual编译成CHM后导航栏目录变成乱码(??????)的解决方案
- 剪辑音乐要很久?3行代码Python瞬间搞定