聊聊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. uniapp/微信小程序 项目day03

    一.商品列表 1.1 获取数据 首先能够进入商品列表的途径 传的数据有 了解了这个之后就可以开始了,先创建分支 创建编译模式,并分配初试数据 这个时候就可以获取数据了 需要的数据 所以在发起请求之前需 ...

  2. 修改服务器ssh端口

    最近,访问公司虚拟机都需要通过堡垒机才能访问了,觉得麻烦.要想不受该规则限制,也有办法,可以通过修改虚拟机ssh端口解决. 下面做个介绍. 1. 通过堡垒机登录虚拟机 2. 修改虚拟机ssh端口 编辑 ...

  3. .Net6新版本的AssemblyLoadContext 加载程序集和卸载程序集

    准备俩个项目 第一个是控制台 第二个项目是类库 类库项目中只有一个示例class 将类库的代码生成dll 并且设置属性为复制到输出目录 using System.Runtime.Loader; var ...

  4. 833——B题题解

    题目链接 题目大意: 给一串字符串(只包含0~9),定义一个最优子串的定义:如果该子串同字符种类数大于最最多字符出现数,那么这个子串可以被称为最优子串. 解题思路: 大眼一看这个数据范围他用n^2的算 ...

  5. Containerd 如何配置 Proxy?

    前言 在某些 air gap 场景中,往往需要离线或使用代理 (Proxy), 例如: 需要通过 Proxy pull 容器镜像: Docker Hub: docker.io Quay: quay.i ...

  6. Go语言核心36讲42-----io包中接口的好处与优势

    我们在前几篇文章中,主要讨论了strings.Builder.strings.Reader和bytes.Buffer这三个数据类型. 知识回顾 还记得吗?当时我还问过你"它们都实现了哪些接口 ...

  7. FIXMAP内存管理器

    fixed map是被linux kernel用来解决一类问题的机制,这类问题的共同特点是: (1)在很早期的阶段需要进行地址映射,而此时,由于内存管理模块还没有完成初始化,不能动态分配内存,也就是无 ...

  8. 多表查询、Navicat软件、PyMySQL模块

    目录 多表查询.Navicat软件.PyMySQL模块 一.多表查询的两种方法 1.准备工作 2.第一种:连表操作 3.第二种:子查询 总结与结论: 二.多表查询练习题 1.课堂多表查询练习题 2.以 ...

  9. 《吐血整理》高级系列教程-吃透Fiddler抓包教程(35)-Fiddler如何抓取微信小程序的包-下篇

    1.简介 通过前边和宏哥的学习,我们了解到Android 7.0 之后增加了对第三方证书的限制,抓包工具(charles.fiddler等)提供的证书都无法通过校验,也就无法抓取HTTPS请求了,对测 ...

  10. 第三十节:fillder抓取APP数据之小程序

    1.下载fillder ,fillder官网:https://www.telerik.com/fiddler 2.安装好后设置fillder: 工具->选项,打开设置面板.选择HTTPS选项卡. ...