【JDK】JDK源码分析-AbstractQueuedSynchronizer(1)
概述
前文「JDK源码分析-Lock&Condition」简要分析了 Lock 接口,它在 JDK 中的实现类主要是 ReentrantLock (可译为“重入锁”)。ReentrantLock 的实现主要依赖于其内部的一个嵌套类 Sync,而 Sync 又继承自 AbstractQueuedSynchronizer (简称 AQS)。而且,不仅 ReentrantLock,其他一些并发工具类如 CountdownLatch、CyclicBarrier 等,其实现也都是基于 AQS 类。AQS 可以理解为并发包中许多类实现的基石。因此,在分析并发包中常用类的实现原理前,有必要先理解一下 AQS,之后再分析的时候就会简单不少。
AQS 内部有一个核心变量 state;此外,以 Node 类为节点维护了两种队列:主队列(main queue)和条件队列(condition queue),简单起见,分别可以将二者理解为双链表和单链表。
AQS 就像是提供了一套基础设施的设备,其它常用类如 ReentrantLock、CountdownLatch 等的内部嵌套类 Sync,都是在 AQS 提供的基础设施之上制定了自己的“游戏规则”,进而生产出了不同的产品。而它们的游戏规则都是围绕 state 变量和这两种队列进行操作的。
PS: 由于 AQS 内容较多,因此打算分多篇文章进行分析,本文先对其整体进行概述。
代码分析
AQS 类签名:
public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {}
可以看到它是一个抽象类,不能直接被实例化。它的父类 AbstractOwnableSynchronizer 的主要代码如下:
public abstract class AbstractOwnableSynchronizer
implements java.io.Serializable { /**
* The current owner of exclusive mode synchronization.
*/
private transient Thread exclusiveOwnerThread; // 其他代码
}
其内部主要维护了一个变量 exclusiveOwnerThread,作用是标记独占模式下的 Owner 线程,后面涉及到的时候再进行分析。
嵌套类
AQS 内部有两个嵌套类,分别为 Node 和 ConditionObject。
Node 类代码如下:
static final class Node {
// 共享模式
static final Node SHARED = new Node();
// 独占模式
static final Node EXCLUSIVE = null; // waitStatus的几种状态
static final int CANCELLED = 1;
static final int SIGNAL = -1;
static final int CONDITION = -2;
static final int PROPAGATE = -3;
volatile int waitStatus; // 前驱节点(主队列)
volatile Node prev;
// 后继节点(主队列)
volatile Node next;
// 节点的线程
volatile Thread thread;
// 后继节点(条件队列)
Node nextWaiter; final boolean isShared() {
return nextWaiter == SHARED;
} 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;
}
}
添加到主队列用的是第二个构造器,Node 类可以理解为对线程 Thread 的封装。因此,在主队列中排队的一个个节点可以理解为一个个有模式(mode)、有状态(waitStatus)的线程。
嵌套类 ConditionObject:
public class ConditionObject implements Condition, java.io.Serializable {
/** First node of condition queue. */
private transient Node firstWaiter; /** Last node of condition queue. */
private transient Node lastWaiter;
// ...
}
ConditionObject 实现了 Condition 接口,它主要操作的是条件队列,这里只贴了其类签名和头尾节点,后面用到的时候再具体分析。
主要变量
AQS 代码虽长,但它的成员变量却不多,如下:
// 主队列头节点
private transient volatile Node head; // 主队列尾结点
private transient volatile Node tail; // 状态,AQS 维护的一个核心变量
private volatile int state;
其中,head 和 tail 为主队列的头尾节点,state 为 AQS 维护的核心变量,ReentrantLock 等类中的 Sync 类实现,都是通过操作 state 来实现各自功能的。
CAS 操作
AQS 内部通过 Unsafe 类实现了一系列 CAS (Compare And Swap) 操作(有关 CAS 的概念这里不再详解,可自行搜索了解):
// 获取 Unsafe 实例
private static final Unsafe unsafe = Unsafe.getUnsafe();
// state、head、tail 等变量的内存偏移地址
private static final long stateOffset;
private static final long headOffset;
private static final long tailOffset;
private static final long waitStatusOffset;
private static final long nextOffset;
static {
try {
stateOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("state"));
headOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("head"));
tailOffset = unsafe.objectFieldOffset
(AbstractQueuedSynchronizer.class.getDeclaredField("tail"));
waitStatusOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("waitStatus"));
nextOffset = unsafe.objectFieldOffset
(Node.class.getDeclaredField("next"));
} catch (Exception ex) { throw new Error(ex); }
} // 一些 CAS 操作
private final boolean compareAndSetHead(Node update) {
return unsafe.compareAndSwapObject(this, headOffset, null, update);
} private final boolean compareAndSetTail(Node expect, Node update) {
return unsafe.compareAndSwapObject(this, tailOffset, expect, update);
} private static final boolean compareAndSetWaitStatus(Node node,
int expect,
int update) {
return unsafe.compareAndSwapInt(node, waitStatusOffset,
expect, update);
} private static final boolean compareAndSetNext(Node node,
Node expect,
Node update) {
return unsafe.compareAndSwapObject(node, nextOffset, expect, update);
}
AQS 内部的许多操作是通过 CAS 来实现线程安全的。
小结
1. AQS 是一个抽象类,无法直接进行实例化;
2. AQS 内部维护了一个核心变量 state,以及两种队列:主队列(main queue)和条件队列(condition queue);
3. AQS 提供了一套基础设施,ReentrantLock 等类通常用一个内部嵌套类 Sync 继承 AQS,并在 Sync 类中制定自己的“游戏规则”。
本文仅对 AQS 做了概述,后面再详细分析实现原理。此外,还有一个类 AbstractQueuedLongSynchronizer,它与 AQS 基本完全一样,区别在于前者的 state 变量为 long 类型,而 AQS 为 int 类型,不再单独进行分析。
PS: 有几篇文章写得也不错,链接如下:
https://www.cnblogs.com/liuyun1995/p/8400663.html
Stay hungry, stay foolish.
PS: 本文首发于微信公众号【WriteOnRead】。
【JDK】JDK源码分析-AbstractQueuedSynchronizer(1)的更多相关文章
- 【JDK】JDK源码分析-AbstractQueuedSynchronizer(2)
概述 前文「JDK源码分析-AbstractQueuedSynchronizer(1)」初步分析了 AQS,其中提到了 Node 节点的「独占模式」和「共享模式」,其实 AQS 也主要是围绕对这两种模 ...
- 【JDK】JDK源码分析-AbstractQueuedSynchronizer(3)
概述 前文「JDK源码分析-AbstractQueuedSynchronizer(2)」分析了 AQS 在独占模式下获取资源的流程,本文分析共享模式下的相关操作. 其实二者的操作大部分是类似的,理解了 ...
- JDK Collection 源码分析(2)—— List
JDK List源码分析 List接口定义了有序集合(序列).在Collection的基础上,增加了可以通过下标索引访问,以及线性查找等功能. 整体类结构 1.AbstractList 该类作为L ...
- JDK AtomicInteger 源码分析
@(JDK)[AtomicInteger] JDK AtomicInteger 源码分析 Unsafe 实例化 Unsafe在创建实例的时候,不能仅仅通过new Unsafe()或者Unsafe.ge ...
- 设计模式(十八)——观察者模式(JDK Observable源码分析)
1 天气预报项目需求,具体要求如下: 1) 气象站可以将每天测量到的温度,湿度,气压等等以公告的形式发布出去(比如发布到自己的网站或第三方). 2) 需要设计开放型 API,便于其他第三方也能接入气象 ...
- AQS框架源码分析-AbstractQueuedSynchronizer
前言:AQS框架在J.U.C中的地位不言而喻,可以说没有AQS就没有J.U.C包,可见其重要性,因此有必要对其原理进行详细深入的理解. 1.AQS是什么 在深入AQS之前,首先我们要搞清楚什么是AQS ...
- JDK Collection 源码分析(3)—— Queue
@(JDK)[Queue] JDK Queue Queue:队列接口,对于数据的存取,提供了两种方式,一种失败会抛出异常,另一种则返回null或者false. 抛出异常的接口:add,remove ...
- JDK Collection 源码分析(1)—— Collection
JDK Collection JDK Collection作为一个最顶层的接口(root interface),JDK并不提供该接口的直接实现,而是通过更加具体的子接口(sub interface ...
- 【JDK】JDK源码分析-ReentrantLock
概述 在 JDK 1.5 以前,锁的实现只能用 synchronized 关键字:1.5 开始提供了 ReentrantLock,它是 API 层面的锁.先看下 ReentrantLock 的类签名以 ...
随机推荐
- 【JVM】虚拟机初见-运行时数据区图解
本文是听咕泡XX公开课视频整理的笔记,较书本更为总结,感谢. 计算机模型(汇编知识):数据集(数据).指令集(操作指令,+-等).控制集(分支循环) JVM运行时的数据区: 程序计数器(每个线程都有) ...
- ThinkPHP判断post,get操作
define('REQUEST_METHOD',$_SERVER['REQUEST_METHOD']); define('IS_GET', REQUEST_METHOD =='GET' ? true ...
- Singleton and Prototype Bean Scope in Spring
Scope描述的是Spring容器如何新建Bean的实例的. 1> Singleton: 一个Spring容器只有一个Bean的实例,此为Spring的默认配置,全容器共享一个实例. 2> ...
- Golang之mirco框架部分浅析
在实习中使用 micro 框架,但是挺多不懂的,看了部分源码搞懂了一些,还是有一些比较复杂没搞懂. 第一部分:初始化 service 并修改端口 main.go // waitgroup is a h ...
- Python爬虫入门:爬取豆瓣电影TOP250
一个很简单的爬虫. 从这里学习的,解释的挺好的:https://xlzd.me/2015/12/16/python-crawler-03 分享写这个代码用到了的学习的链接: BeautifulSoup ...
- TypeScript算法与数据结构-队列和循环队列
本文涉及的源码,均在我的github.有两部分队列和循环队列.有问题的可以提个issue,看到后第一时间回复 1. 队列(Queue) 队列也是一种线性的数据结构, 队列是一种先进先出的数据结构.类似 ...
- 【题解】旅行-C++
Description 某趟列车的最大载客容量为V人,沿途共有n个停靠站,其中始发站为第1站,终点站为第n站.在第1站至第n-1站之 间,共有m个团队申请购票搭乘,若规定:(1)对于某个团队的购票申请 ...
- 文件传输——TCP/IP协议介绍总结
一.链路层 数据链路层的工作特性: 1.为IP模块发送和接收IP数据报2.为ARP模块发送ARP请求和接收ARP应答(ARP:地址解析协议,将IP地址转换成MAC地址)3.为RARP发送RARP请求和 ...
- VNC服务配置
Windows通过VNC远程桌面访问Ubuntu设备:一个WIN7的机器,一个系统为Ubuntu的机器 (二者可以均可为虚拟机或实体机) 目的:在Windows系统上通过VNC远程访问Ubuntu. ...
- idea万能快捷键(alt enter),你不知道的17个实用技巧!!!
说明 IDEA里有一个万能快捷键(alt enter),功能非常强大,同一个快捷键,可以根据不同的语境提示你不同的操作, 很多人可能还不了解这些功能,在处理代码的时候还手动处理,了解这些技巧之后,你编 ...