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相关推荐

  1. 深入java并发包源码(三)AQS独占方法源码分析

    深入java并发包源码(一)简介 深入java并发包源码(二)AQS的介绍与使用 深入java并发包源码(三)AQS独占方法源码分析 AQS 的实现原理 学完用 AQS 自定义一个锁以后,我们可以来看 ...

  2. Java并发包基石-AQS详解

    目录 1 基本实现原理 1.1 如何使用 1.2 设计思想 2 自定义同步器 2.1 同步器代码实现 2.2 同步器代码测试 3 源码分析 3.1 Node结点 3.2 独占式 3.3 共享式 4 总 ...

  3. Java并发包源码学习之AQS框架(三)LockSupport和interrupt

    接着上一篇文章今天我们来介绍下LockSupport和Java中线程的中断(interrupt). 其实除了LockSupport,Java之初就有Object对象的wait和notify方法可以实现 ...

  4. java并发包详解(jdk7)

    在此对java并发包做一个大致总结,如有错误,请指正. juc包的总体结构大致如下 外层框架主要有Lock(ReentrantLock.ReadWriteLock等).同步器(semaphores等) ...

  5. Java并发包源码学习系列:同步组件CountDownLatch源码解析

    文章目录 CountDownLatch概述 使用案例与基本思路 类图与基本结构 void await() boolean await(long timeout, TimeUnit unit) void ...

  6. juc是个什么鬼(一) Java并发包详情,CAS分析,解决ABA问题

    JUC就是java.util.concurrent包,俗称java并发包 通过看JDK的API,我们发现JUC下有俩子包,分别是atomic和locks包,这篇文章重点就是看这两个包下的内容 Atom ...

  7. 腾讯面试题Java 并发包之线程池综述

    Java 并发包之线程池综述 ■ 线程池的创建 在Java中,您可以通过调整-Xss参数来调节每个线程栈的大小(64bit系统默认1024KB),当减小该值时意味着可以创建更多的线程数,但问题是JVM ...

  8. 死磕java并发cas_死磕 java并发包之AtomicInteger源码分析

    问题 (1)什么是原子操作? (2)原子操作和数据库的ACID有啥关系? (3)AtomicInteger是怎么实现原子操作的? (4)AtomicInteger是有什么缺点? 简介 AtomicIn ...

  9. java并发包系列---LockSupport

    长久以来对线程阻塞与唤醒经常我们会使用object的wait和notify,除了这种方式,java并发包还提供了另外一种方式对线程进行挂起和恢复,它就是并发包子包locks提供的LockSupport ...

最新文章

  1. 连接网络计算机后用户名更改不,电脑修改ip后无法上网
  2. [转]ASP.NET 缓存(十六)--检索缓存项的值
  3. VMware View 与Citrix XenDesktop对决之用户体验篇
  4. python 编译exe
  5. linux下面子目录绑定域名的方法,.htaccess绑定子域名到子目录方法
  6. Silverlight MMORPG团队项目截图
  7. JavaScript常用内置对象之Array
  8. 透明的WinForm窗体
  9. 微信支付PKIX path building failed
  10. JPA EntityManagers,事务及其周围的一切
  11. jsp oracle连接池,利用Oracle自带的连接池类的一例
  12. [Java] 蓝桥杯ALGO-139 算法训练 s01串
  13. c语言数组元素前移后移,如何将一个数组的元素循环左移?
  14. 免费证件照制作的软件有哪些?来看看这几个好用的软件
  15. 在线教育平台项目——设计 接口定义
  16. 为你的TabBar添加Badge-感谢分享
  17. 自动驾驶——Smooth Local Planning
  18. C#,动态规划问题中基于单词搜索树(Trie Tree)的单词断句分词( Word Breaker)算法与源代码
  19. C语言手机通讯录系统
  20. 算法:数字的排列组合问题

热门文章

  1. java保留两位小数并向上取整_Java取整,固定保留两位小数,适配负数、金融数字。...
  2. BZOJ 3309: DZY Loves Math
  3. 面试宝典 | 不完全总结
  4. 你坚持的哪些细小的好习惯
  5. 适用于通达信软件,股票两高点划线的代码
  6. Matplotlib 绘制 双轴 图
  7. 批量的将excel转换成pdf格式的方法
  8. Redis关闭持久化
  9. Help and Manual编译成CHM后导航栏目录变成乱码(??????)的解决方案
  10. 剪辑音乐要很久?3行代码Python瞬间搞定