聊聊JUC包下的底层支撑类-AbstractQueuedSynchronizer(AQS)

juc包下的一堆并发工具类是我们日常开发特别是面试中常被拿来问的八股文之一,为了工作也好,为了面试也罢,今天开始想尝试着把这些给大伙描述明白,所以开始写下这篇博文,如果后续要涉及每个常用类的源码的话可能会是一个系列,计划从比较底层的AQS聊起,然后结合ReentrantLock的源码来聊AQS独占锁的具体实现,以及加锁和释放锁的过程;然后再聊聊JUC包下其他类如CountDownLatch、CyclicBarrier、Phaser、ReadWriteLock、Semaphore、Exchanger以及LockSupport的使用和原理,有时间的话再结合CountDownLatch的源码来聊AQS共享锁的具体实现,接下来话不多说开始踏上揭秘AQS之旅

一、AQS是什么

  • AQS全称AbstractQueuedSynchronizer即抽象队列同步器,可以理解成是一个可以实现锁的框架(基类),它可以实现共享和独占两种模式的锁,事实上juc包下很多关于锁的工具类也是基于AQS的;它提供了一些模板方法供子类实现拓展,并且本身结合底层的Unsafe类实现了基于cpu原语层的安全操作,从而实现在并发环境下的线程安全

二、AQS的实现原理

  • AQS能作为基类来实现锁的功能主要原因来自于它维护的一个int类型的state变量和一个FIFO的双向队列;实现类可以根据自身的需求,通过控制state的值来决定线程是否需要阻塞,而双向队列用来存放没有争抢到锁资源的线程;并且AQS通过结合Unsafe类的能力封装了可以线程安全的操作state值的方法(一堆CAS的操作方法),这样程序员就可以只关注锁的使用而不必关注底层实现的细节了;
  • AQS支持两种模式锁的实现,分别是独占锁和共享锁,独占锁的具体实现以ReentrantLock为代表,共享锁的实现诸如CountDownLatch、CyclicBarrier等

注:由于后续会介绍AQS的源码以及子类实现,所以这里只是大白话般的描述了一下AQS的原理,即两个关键:一个state一个双向队列,其实要展开还有许多细节要聊,考虑到这些细节后续源码里会有体现这里就不再表述了

三、AQS的源码简析

1、state变量

private volatile int state;

1、state是AQS提供的供子类拓展的一个同步状态,子类可以维护state的不同状态来实现不同效果的锁实现,如ReentrantLock就是通过维护state是否为0或1来表示锁的加解操作;

2、用volatile修饰主要是为了在并发环境下线程可见

2、Node内部类

  • Node类是双向CLH队列的构成元素,其维护的thread变量就是没有争抢到锁的线程,然后还维护了CLH队列的其他几个关键信息,如当前Node的前置节点(prev)、后续节点(next)等,下面贴上Node的源码
static final class Node {
/** 表示当前节点正处于共享模式 */
static final Node SHARED = new Node();
/** 表示当前节点正处于独占模式 */
static final Node EXCLUSIVE = null; /** waitStatus对应的值,表示线程已取消 */
static final int CANCELLED = 1;
/** waitStatus对应的值,指示后续线程需要取消标记*/
static final int SIGNAL = -1;
/** waitStatus对应的值,指示线程正在等待condition唤醒*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
*/
static final int PROPAGATE = -3; /**
* 等待状态,枚举值有1,0,-1,-2,-3分别对应上面的几个变量值,0表示以上状态都不是
*/
volatile int waitStatus; /**
* 当前Node的前置节点
*/
volatile Node prev; /**
* 当前Node的后继节点
*/
volatile Node next; /**
* 与当前Node绑定的被阻塞的线程
*/
volatile Thread thread; /**
* 下一个等待节点,Condition状态下要用到
*/
Node nextWaiter; /**
* 如果节点在共享模式下等待,则返回true。
*/
final boolean isShared() {
return nextWaiter == SHARED;
} /**
* 获取当前队列的前置节点
*
* @return the predecessor of this node
*/
final Node predecessor() throws NullPointerException {
Node p = prev;
if (p == null)
throw new NullPointerException();
else
return p;
} Node() { // Used to establish initial head or SHARED marker
} Node(Thread thread, Node mode) { // Used by addWaiter
this.nextWaiter = mode;
this.thread = thread;
} Node(Thread thread, int waitStatus) { // Used by Condition
this.waitStatus = waitStatus;
this.thread = thread;
}
}

3、AQS供子类拓展的模板方法

独占模式供子类实现的方法

  • tryAcquire(int) 尝试获取锁,获取成功返回true,失败返回false

  • tryRelease(int) 尝试释放锁,释放成功返回true,失败返回false

共享模式供子类实现的方法

  • tryAcquireshared(int)

    尝试获取锁,负数表示失败; 0表示功,但没有剩余可用资源:正数表示成功,且有剩余资源。

  • tryReleaseshared(int)

    尝试释放锁,成功返回true,失败返回false

上面简单介绍了一下AQS的原理以及源码的部分注释,接下来我会写一篇ReentrantLock源码解读的相关文章,来体验下AQS的实际用处

聊聊JUC包下的底层支撑类-AbstractQueuedSynchronizer(AQS)的更多相关文章

  1. juc包:使用 juc 包下的显式 Lock 实现线程间通信

    一.前置知识 线程间通信三要素: 多线程+判断+操作+通知+资源类. 上面的五个要素,其他三个要素就是普通的多线程程序问题,那么通信就需要线程间的互相通知,往往伴随着何时通信的判断逻辑. 在 java ...

  2. Java语言Lang包下常用的工具类介绍_java - JAVA

    文章来源:嗨学网 敏而好学论坛www.piaodoo.com 欢迎大家相互学习 无论你在开发哪中 Java 应用程序,都免不了要写很多工具类/工具函数.你可知道,有很多现成的工具类可用,并且代码质量都 ...

  3. java扫描某个包下的所有java类并加载

    最近在学习java的反射和注解,实际情景中需要扫描某个包下的所有java类,然后使用类加载器加载类. 基本思路,获得程序的路径扫描src下某个包内的子包和java类,实现也比较简单. 运行环境:win ...

  4. JUC 包下工具类,它的名字叫 LockSupport !你造么?

    前言 LockSupport 是 JUC 中常用的一个工具类,主要作用是挂起和唤醒线程.在阅读 JUC 源码中经常看到,所以很有必要了解一下. 公众号:liuzhihangs ,记录工作学习中的技术. ...

  5. 【Java多线程】JUC包下的工具类CountDownLatch、CyclicBarrier和Semaphore

    前言 JUC中为了满足在并发编程中不同的需求,提供了几个工具类供我们使用,分别是CountDownLatch.CyclicBarrier和Semaphore,其原理都是使用了AQS来实现,下面分别进行 ...

  6. JUC(三):JUC包下锁概念

    线程不安全集合类 ArrayList List是线程不安全的集合类,底层是Object数组实现,初始化容量是10(其实是一个空数组,第一次扩容时,将数组扩容为10),其后每次扩容大小为当前容量的一半( ...

  7. java多线程系列11 juc包下的队列

    队列分为两类  阻塞队列 BlockingQueue提供如下两个支持阻塞的方法:   (1)put(E e): 尝试把e元素放如BlockingQueue中,如果该队列的元素已满,则阻塞该线程.   ...

  8. Concurrent包下用过哪些类?

    1.executor接口,使用executor接口的子接口ExecutorService用来创建线程池2.Lock接口下的ReentrantLock类,实现同步,比如三个线程循环打印ABCABCABC ...

  9. JUC包下Semaphore学习笔记

    在Java的并发包中,Semaphore类表示信号量.Semaphore内部主要通过AQS(AbstractQueuedSynchronizer)实现线程的管理.Semaphore有两个构造函数,参数 ...

  10. JUC包下CyclicBarrier学习笔记

    CyclicBarrier,一个同步辅助类,在API中是这么介绍的: 它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point).在涉及一组固定大小的线程的程序中,这 ...

随机推荐

  1. JS逆向实战4--cookie——__jsl_clearance_s 生成

    分析 网站返回状态码521,从浏览器抓包来看,浏览器一共对此地址请求了三次(中间是设置cookie的过程): 第一次请求:网站返回的响应状态码为 521,响应返回的为经过 混淆的 JS 代码:但是这些 ...

  2. 纯css爱心代码-最近超级火的打火机与公主裙中的爱心代码(简易版)

    theme: cyanosis 最近打火机与公主裙中的爱心代码超级火,看着特别心动,让俺用css来写个简易版!!! 先看效果: 代码拆解: 主要是分为3大部分 分子颗粒 爱心 动画 代码实现: 分子颗 ...

  3. 使用@Transactional注解的方法所在的类获取不到注解的解决方案

    前段时间遇到一个问题,一个service叫做A吧,有多个实现类分别是B,C,D,需要根据前端传的不同参数去匹配不同的实现类,我就自定义了一个注解@OrderDeal放在B,C,D上面,然后匹配前端传的 ...

  4. docker安装消息队列(rabbitmq)及数据库(mongo、mysql)

    解决ipv6 访问问题 nohup socat TCP6-LISTEN:36001,reuseaddr,fork TCP4:127.0.0.1:36000 > /root/ip6to4.log ...

  5. [ PHP 内核与扩展开发系列] 内存管理 —— 引用计数

    对于 PHP 这种需要同时处理多个请求的程序来说,申请和释放内存的时候应该慎之又慎,一不小心便会酿成大错.另一方面,除了要安全申请和释放内存外,还应该做到内存的最小化使用,因为它可能要处理每秒钟数以千 ...

  6. 【Java集合框架002】原理层面:HashMap全解析

    一.前言 二.HashMap 2.1 HashMap数据结构 + HashMap线程不安全 + 哈希冲突 2.1.1 HashMap数据结构 学习的时候,先整体后细节,HashMap整体结构是 底层数 ...

  7. UBOOT编译--- UBOOT编译过程目标依赖分析(八)

    1. 前言 UBOOT版本:uboot2018.03,开发板myimx8mmek240. 2. 概述 本文采用自顶向下的方法,从顶层目标开始到最原始的依赖. 3. u-boot编译 uboot的编译分 ...

  8. Python异步爬虫(aiohttp版)

    异步协程不太了解的话可以去看我上篇博客:https://www.cnblogs.com/Red-Sun/p/16934843.html PS:本博客是个人笔记分享,不需要扫码加群或必须关注什么的(如果 ...

  9. L1-064 估值一亿的AI核心代码 (20分)

    L1-064 估值一亿的AI核心代码 (20分) 以上图片来自新浪微博. 本题要求你实现一个稍微更值钱一点的 AI 英文问答程序,规则是: 无论用户说什么,首先把对方说的话在一行中原样打印出来: 消除 ...

  10. TortoiseGit (小乌龟安装配置及使用)

    TortoiseGit 使用教程 (图文详解) 第一步 下载Git:下载地址:https://gitforwindows.org 第二步 下载TortoiseGit 以及 汉化包 (安装教程这里就不详 ...