JUC组件逐层剥离与深度剖析
- 前言
- JUC组件逐层剥离与深度剖析
-
- [1. AQS 核心设计思想与底层数据结构](#1. AQS 核心设计思想与底层数据结构)
-
- [1.1 同步状态量 `state`](#1.1 同步状态量
state) - [1.2 CLH 队列节点 `Node` 内部类](#1.2 CLH 队列节点
Node内部类)
- [1.1 同步状态量 `state`](#1.1 同步状态量
- [2. OpenJDK 8 AQS 核心源码逐行剖析](#2. OpenJDK 8 AQS 核心源码逐行剖析)
-
- [2.1 独占模式获取锁(Acquire)](#2.1 独占模式获取锁(Acquire))
-
- [入队操作:`addWaiter` 与 `enq`](#入队操作:
addWaiter与enq) - 队列自旋与阻塞:`acquireQueued`
- 状态检查与挂起:`shouldParkAfterFailedAcquire`
- [入队操作:`addWaiter` 与 `enq`](#入队操作:
- [2.2 独占模式释放锁(Release)](#2.2 独占模式释放锁(Release))
- [2.3 共享模式获取与传播机制(Shared Mode)](#2.3 共享模式获取与传播机制(Shared Mode))
-
- [传播性核心实现:`setHeadAndPropagate` 与 JDK-6801020 Bug 修复](#传播性核心实现:
setHeadAndPropagate与 JDK-6801020 Bug 修复)
- [传播性核心实现:`setHeadAndPropagate` 与 JDK-6801020 Bug 修复](#传播性核心实现:
- [2.4 条件变量:`ConditionObject` 条件队列](#2.4 条件变量:
ConditionObject条件队列)
- [3. AQS 如何支撑四种典型 J.U.C 组件](#3. AQS 如何支撑四种典型 J.U.C 组件)
-
- [3.1 `ReentrantLock` (可重入互斥锁)](#3.1
ReentrantLock(可重入互斥锁)) -
- [公平锁 vs 非公平锁源码实现对比](#公平锁 vs 非公平锁源码实现对比)
- 公平性关键方法:`hasQueuedPredecessors()`
- [3.2 `Semaphore` (信号量)](#3.2
Semaphore(信号量)) - [3.3 `CountDownLatch` (倒计数器)](#3.3
CountDownLatch(倒计数器)) - [3.4 `ReentrantReadWriteLock` (读写锁)](#3.4
ReentrantReadWriteLock(读写锁))
- [3.1 `ReentrantLock` (可重入互斥锁)](#3.1
- [4. J.U.C 组件在 AQS 钩子中的实现映射总结](#4. J.U.C 组件在 AQS 钩子中的实现映射总结)
- [5. 内存模型与底层物理原语支持](#5. 内存模型与底层物理原语支持)
前言
本文旨在记录近期研读Java源码的学习心得与疑难问题。由于个人理解水平有限,文中内容难免存在疏漏,恳请读者不吝指正。
JUC组件逐层剥离与深度剖析
AbstractQueuedSynchronizer(简称 AQS,位于 java.util.concurrent.locks 包下)是 Java 并发工具包(J.U.C)的基石。AQS 采用模板方法设计模式(Template Method Pattern),抽象了同步状态管理、线程阻塞与唤醒机制以及同步队列维护,将具体的资源获取与释放逻辑(如独占/共享、公平/非公平)留给子类实现。
下面结合 OpenJDK 8 的底层源码,从数据结构、核心算法、模板方法到具体的 J.U.C 衍生组件进行逐层剥离与深度解析。
1. AQS 核心设计思想与底层数据结构
AQS 的底层架构核心由三部分构成:一个 volatile 修饰的整型同步状态量(state) 、一个基于变体 CLH 锁的虚拟双向 FIFO 阻塞队列 ,以及基于 LockSupport / Unsafe 原语的线程挂起与唤醒机制。
+---------------+
| state (int) |
+---------------+
^
| CAS 操作 (Unsafe)
v
+------+ next +------+ next +------+
head| Node | ------> | Node | ------> | Node | tail
| | <------ | | <------ | |
+------+ prev +------+ prev +------+
(Dummy Node) (Thread A) (Thread B)
1.1 同步状态量 state
AQS 内部维持一个 state 变量,用来表示同步状态。
java
/**
* 同步状态。使用 volatile 保证多线程间的可见性。
* 通过 CAS (compareAndSetState) 原子操作保证状态变更的原子性。
*/
private volatile int state;
protected final int getState() {
return state;
}
protected final void setState(int newState) {
state = newState; // volatile 写,自带 StoreStore / StoreLoad 屏障语义
}
protected final boolean compareAndSetState(int expect, int update) {
// 调用 Unsafe 的 CAS 原理直接更新内存偏移量处的 int 值
return unsafe.compareAndSetInt(this, stateOffset, expect, update);
}
1.2 CLH 队列节点 Node 内部类
AQS 内部维护了一个双向链表队列。Node 节点是对竞争资源的线程的封装。
java
static final class Node {
/** 标识节点处于共享模式 */
static final Node SHARED = new Node();
/** 标识节点处于独占模式 */
static final Node EXCLUSIVE = null;
/** 节点等待状态值:CANCELLED (1) - 线程超时或被中断,已取消获取请求 */
static final int CANCELLED = 1;
/** 节点等待状态值:SIGNAL (-1) - 表示后继节点处于挂起状态,当前节点释放锁或取消时必须唤醒后继节点 */
static final int SIGNAL = -1;
/** 节点等待状态值:CONDITION (-2) - 节点处于 Condition 条件等待队列中 */
static final int CONDITION = -2;
/** 节点等待状态值:PROPAGATE (-3) - 共享模式下,无条件传播 releaseShared 唤醒信号 */
static final int PROPAGATE = -3;
/**
* 节点当前的状态,初始值为 0。
* 可选值为 CANCELLED, SIGNAL, CONDITION, PROPAGATE,或者 0(非上述特殊状态)。
*/
volatile int waitStatus;
/** 前驱节点引用 */
volatile Node prev;
/** 后继节点引用 */
volatile Node next;
/** 绑定当前节点的线程 */
volatile Thread thread;
/**
* 链接下一个等待条件的节点(用于 Condition 队列),
* 或者在同步队列中用作模式标记(SHARED 标识 shared 模式)。
*/
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() { // 用于建立初始 head 哑节点或 SHARED 标记
}
Node(Thread thread, Node mode) { // 用于 addWaiter 方法
this.nextWaiter = mode;
this.thread = thread;
}
Node(Thread thread, int waitStatus) { // 用于 Condition 队列
this.waitStatus = waitStatus;
this.thread = thread;
}
}
2. OpenJDK 8 AQS 核心源码逐行剖析
2.1 独占模式获取锁(Acquire)
独占锁获取入口方法为 acquire(int arg),它是一个模板方法,整体流程为:tryAcquire 抢锁 → \rightarrow → 失败则 addWaiter 入队 → \rightarrow → acquireQueued 自旋阻塞等待。
java
public final void acquire(int arg) {
// 1. tryAcquire 由子类实现(如 ReentrantLock),尝试原子获取锁
// 2. 若 tryAcquire 失败,执行 addWaiter 将当前线程构建为 EXCLUSIVE 节点入队
// 3. acquireQueued 让节点在队列中自旋尝试抢锁或进入 park 挂起
// 4. 若 acquireQueued 返回 true(表示期间响应过中断),由于该过程不抛出中断异常,此处补发中断标记
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
入队操作:addWaiter 与 enq
java
private Node addWaiter(Node mode) {
// 封装当前线程为 Node 节点
Node node = new Node(Thread.currentThread(), mode);
// Try the fast path of enq; backup to full enq on failure
Node pred = tail;
// 快速路径:如果尾节点不为空,直接 CAS 尝试追加到队尾
if (pred != null) {
node.prev = pred;
if (compareAndSetTail(pred, node)) {
pred.next = node; // 建立双向指针
return node;
}
}
// 慢速路径:尾节点为空(队列未初始化)或 CAS 竞争失败,进入无界自旋入队
enq(node);
return node;
}
private Node enq(final Node node) {
for (;;) { // 死循环,保证 CAS 成功才退出
Node t = tail;
if (t == null) { // Must initialize:队首延时初始化,设置 Head 哑节点(Dummy Node)
if (compareAndSetHead(new Node()))
tail = head; // head 和 tail 初始均指向这个 Dummy 节点
} else {
node.prev = t;
// 通过 CAS 竞争设置队尾,确保多线程并发入队时的安全性
if (compareAndSetTail(t, node)) {
t.next = node; // 成功设置 tail 后,绑定前驱节点的 next 指针
return t;
}
}
}
}
注意:
node.prev = t发生在 CAS 前,而t.next = node发生在 CAS 成功后。因此,在任何时刻从tail向prev遍历队列是绝对安全的,而从head向next遍历可能遇到next暂未赋值为null的中间状态。
队列自旋与阻塞:acquireQueued
java
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) { // 死循环:自旋 + 挂起
final Node p = node.predecessor(); // 获取前驱节点
// 只有前驱节点是 head,当前节点才有资格调用 tryAcquire 抢锁(体现 FIFO 顺序)
if (p == head && tryAcquire(arg)) {
setHead(node); // 抢锁成功,当前节点升为新的 head(成为 Dummy Node)
p.next = null; // help GC,断开原 head 的 next 指针
failed = false;
return interrupted; // 返回在等待过程中是否发生了中断
}
// 抢锁失败后,判断前驱节点状态以决定是否应该挂起当前线程
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt()) // 调用 LockSupport.park 阻塞,唤醒后返回中断状态
interrupted = true;
}
} finally {
if (failed) // 异常退出(如产生未捕获异常)时取消该节点的抢锁请求
cancelAcquire(node);
}
}
private void setHead(Node node) {
head = node;
node.thread = null; // Head 节点的 thread 永远为 null
node.prev = null;
}
状态检查与挂起:shouldParkAfterFailedAcquire
java
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
int ws = pred.waitStatus;
if (ws == Node.SIGNAL)
/*
* 前驱节点状态已经是 SIGNAL(-1),说明前驱节点在释放锁时必然会 unpark 唤醒当前节点,
* 当前节点可以安全地调用 LockSupport.park() 挂起自身。
*/
return true;
if (ws > 0) {
/*
* 前驱节点 ws > 0(即 CANCELLED=1),说明前驱节点已取消。
* 跳过所有连续已取消的前驱节点,将 node.prev 重新链接到一个未取消节点上。
*/
do {
node.prev = pred = pred.prev;
} while (pred.waitStatus > 0);
pred.next = node;
} else {
/*
* waitStatus 为 0 或 PROPAGATE (-3)。
* 使用 CAS 将前驱节点的 waitStatus 设置为 SIGNAL (-1),
* 告诉前驱节点"你释放锁时记得唤醒我"。本次自旋不挂起,下一次自旋再挂起。
*/
compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
}
return false;
}
private final boolean parkAndCheckInterrupt() {
LockSupport.park(this); // 线程在此处阻塞,等待 unpark 或 interrupt 唤醒
return Thread.interrupted(); // 被唤醒后清除并返回中断标记
}
2.2 独占模式释放锁(Release)
java
public final boolean release(int arg) {
// 1. 调用子类的 tryRelease,尝试释放状态 state
if (tryRelease(arg)) {
Node h = head;
// 头节点不为空且 waitStatus != 0(说明有后继节点需要被 SIGNAL 唤醒)
if (h != null && h.waitStatus != 0)
unparkSuccessor(h); // 唤醒后继节点
return true;
}
return false;
}
private void unparkSuccessor(Node node) {
int ws = node.waitStatus;
if (ws < 0)
// 重置 head 节点的 waitStatus 为 0,避免重复唤醒
compareAndSetWaitStatus(node, ws, 0);
/*
* 获取待唤醒的后继节点 s。
* 通常是 node.next,但如果 node.next 为空或者被取消(waitStatus > 0),
* 则必须从队尾 tail 倒序向前查找最靠近 node 的有效(waitStatus <= 0)节点。
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread); // 底层调用 Unsafe.unpark 唤醒目标线程
}
2.3 共享模式获取与传播机制(Shared Mode)
共享模式(如 CountDownLatch、Semaphore、ReentrantReadWriteLock 的读锁)允许多个线程同时持有资源。AQS 的核心设计难点在于共享唤醒的传播性(Propagation)。
java
public final void acquireShared(int arg) {
// tryAcquireShared 返回值说明:
// < 0: 获取失败,需要入队等待
// = 0: 获取成功,但无剩余共享资源
// > 0: 获取成功,且有剩余共享资源,可以继续唤醒后继共享节点
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}
private void doAcquireShared(int arg) {
// 将当前线程封装为 SHARED 节点追加到 CLH 队列
final Node node = addWaiter(Node.SHARED);
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head) {
int r = tryAcquireShared(arg);
if (r >= 0) { // 获取共享资源成功
// 设置当前节点为 Head,并且如果 r > 0 或旧/新 head 标记为 PROPAGATE,继续唤醒后继共享节点
setHeadAndPropagate(node, r);
p.next = null; // help GC
if (interrupted)
selfInterrupt();
failed = false;
return;
}
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
传播性核心实现:setHeadAndPropagate 与 JDK-6801020 Bug 修复
java
private void setHeadAndPropagate(Node node, int propagate) {
Node h = head; // 记录旧 head
setHead(node); // 将当前成功获取锁的节点设为新的 head
/*
* 满足以下任一条件时,触发向后传播唤醒:
* 1. propagate > 0:子类明确指示有剩余共享资源。
* 2. h == null 或 h.waitStatus < 0:旧 head 节点处于 SIGNAL 或 PROPAGATE 状态。
* 3. (h = head) == null 或 h.waitStatus < 0:新 head 节点处于 SIGNAL 或 PROPAGATE 状态。
*/
if (propagate > 0 || h == null || h.waitStatus < 0 ||
(h = head) == null || h.waitStatus < 0) {
Node s = node.next;
// 如果后继节点未指定,或者后继节点是共享节点,执行唤醒
if (s == null || s.isShared())
doReleaseShared();
}
}
private void doReleaseShared() {
for (;;) {
Node h = head;
if (h != null && h != tail) {
int ws = h.waitStatus;
if (ws == Node.SIGNAL) {
// 如果当前 head 是 SIGNAL,将其 CAS 重置为 0,防止重复唤醒
if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
continue; // CAS 失败说明被并发修改,重新循环
unparkSuccessor(h); // 唤醒后继节点
}
else if (ws == 0 &&
!compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
// 如果 ws 为 0,说明后继节点尚未将 head 置为 SIGNAL。
// 通过将其转换为 PROPAGATE,确保后续 setHeadAndPropagate 读取时能继续触发传播
continue; // CAS 失败则重试
}
if (h == head) // 如果 head 在执行过程中没发生改变,退出循环
break;
}
}
JDK-6801020 历史背景: 在 Java 6 及之前版本中,
setHeadAndPropagate仅检查propagate > 0。在并发释放/获取极高的情况下,当tryAcquireShared返回0但由于并发releaseShared释放了新资源时,可能会导致挂起在队列中的后续共享节点无法被唤醒(出现死锁)。引入Node.PROPAGATE状态与对h.waitStatus < 0的双重检测,彻底解决了这一并发唤醒丢失问题。
2.4 条件变量:ConditionObject 条件队列
ConditionObject 是 AQS 实现的高效等待/通知机制。它维持了一个单向的条件等待队列 (以 firstWaiter 和 lastWaiter 为头尾)。
AQS 同步队列 (双向):
[Head] <-> [Node A] <-> [Node B] <-> [Tail]
Condition 条件队列 (单向):
[firstWaiter (Node C)] -> [Node D] -> [lastWaiter (Node E)]
等待逻辑:await()
java
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
// 1. 将当前线程封装为 waitStatus = CONDITION 的 Node,加入条件队列末尾
Node node = addConditionWaiter();
// 2. 释放当前线程占有的所有独占锁 state,并记录释放前的 state 值 savedState
int savedState = fullyRelease(node);
int interruptMode = 0;
// 3. 循环检查当前节点是否已经被转移到了 AQS 同步队列中
while (!isOnSyncQueue(node)) {
LockSupport.park(this); // 不在同步队列中,挂起当前线程
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break; // 发生了中断,跳出循环
}
// 4. 节点被 signal() 转移到了 AQS 同步队列后,调用 acquireQueued 重新抢占独占锁
if (acquireQueued(node, savedState) && interruptMode != REINTERRUPT)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null) // 清理条件队列中的非 CONDITION 节点
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
唤醒逻辑:signal()
java
public final void signal() {
if (!isHeldExclusively()) // 必须由独占锁持有者调用
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first); // 转移首个等待节点
}
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && // 尝试将节点转入 AQS 队列
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// 1. 如果 CAS 无法将 waitStatus 从 CONDITION 改为 0,说明节点已被取消
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 2. 将节点通过 enq() 原子追加到 AQS 同步队列末尾,返回其前驱节点 p
Node p = enq(node);
int ws = p.waitStatus;
// 3. 如果前驱节点已被取消,或者设置前驱节点为 SIGNAL 失败,直接 unpark 唤醒该线程进行自旋修复
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
3. AQS 如何支撑四种典型 J.U.C 组件
AQS 采用模板方法,定义了同步器框架的公共行为,而将具体的资源获取/释放逻辑交由子类通过重写以下 protected 钩子方法(Hook Methods)来实现:
| 钩子方法 | 模式 | 描述 |
|---|---|---|
tryAcquire(int arg) |
独占 | 尝试获取独占锁。成功返回 true。 |
tryRelease(int arg) |
独占 | 尝试释放独占锁。成功返回 true。 |
tryAcquireShared(int arg) |
共享 | 尝试获取共享锁。返回 ≥ 0 \ge 0 ≥0 表示成功, 返回 < 0 \text{返回} < 0 返回<0 表示失败。 |
tryReleaseShared(int arg) |
共享 | 尝试释放共享锁。成功返回 true。 |
isHeldExclusively() |
独占 | 判断当前线程是否独占持有着锁。 |
3.1 ReentrantLock (可重入互斥锁)
ReentrantLock 利用 AQS 的 state 记录当前持锁线程的锁重入次数:
state == 0:无锁状态。state > 0:已被线程占用,数值即为重入深度。
公平锁 vs 非公平锁源码实现对比
java
abstract static class Sync extends AbstractQueuedSynchronizer {
// 释放锁对于公平/非公平锁都是一致的
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException(); // 非锁持有者释放抛出异常
boolean free = false;
if (c == 0) { // 完全释放锁
free = true;
setExclusiveOwnerThread(null); // 清除持有者标记
}
setState(c); // 写 volatile 变量 state
return free;
}
}
// 1. 非公平锁实现
static final class NonfairSync extends Sync {
final void lock() {
// 非公平直接插队:上来直接尝试 CAS 抢锁,抢成功就占为己有
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1); // 失败才进入标准 AQS 排队
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 发现 state 为 0,不顾队列中是否有排队线程,直接 CAS 争抢
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 处理可重入逻辑:如果是当前持有线程再次获取
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires; // 累加重入次数
if (nextc < 0) // overflow 检查
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
// 2. 公平锁实现
static final class FairSync extends Sync {
final void lock() {
acquire(1); // 不抢占,严格直接走 AQS acquire
}
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 核心差别:先调用 hasQueuedPredecessors 检查队列中是否有比当前线程等待更久的节点!
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;
}
}
公平性关键方法:hasQueuedPredecessors()
java
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
/*
* 判断条件解析:
* 1. h != t:队列已初始化且至少有一个非 Dummy 节点。
* 2. (s = h.next) == null:说明正有节点在进行 enq 入队并发操作,存在排队节点。
* 3. s.thread != Thread.currentThread():队头第一个有效节点绑定的线程不是当前线程。
*/
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
3.2 Semaphore (信号量)
Semaphore 利用 AQS 共享模式,state 映射为允许同时访问资源的许可数(Permits)。
java
abstract static class Sync extends AbstractQueuedSynchronizer {
Sync(int permits) {
setState(permits); // state 赋值为初始许可数量
}
// 共享释放许可
protected final boolean tryReleaseShared(int releases) {
for (;;) {
int current = getState();
int next = current + releases; // 归还许可
if (next < current) // overflow
throw new Error("Maximum permit count exceeded");
if (compareAndSetState(current, next)) // CAS 保证多线程归还许可的原子性
return true;
}
}
}
// 非公平信号量获取
static final class NonfairSync extends Sync {
protected int tryAcquireShared(int acquires) {
return nonfairTryAcquireShared(acquires);
}
}
final int nonfairTryAcquireShared(int acquires) {
for (;;) {
int available = getState();
int remaining = available - acquires; // 扣减许可
// 若 remaining < 0,获取失败返回负值;若 remaining >= 0 且 CAS 扣减成功,返回剩余许可
if (remaining < 0 ||
compareAndSetState(available, remaining))
return remaining;
}
}
3.3 CountDownLatch (倒计数器)
CountDownLatch 利用 AQS 共享模式,state 映射为倒计数(Count):
- 线程调用
await()时,要求state == 0才能成功获取(否则在 AQS 队列中阻塞)。 - 线程调用
countDown()时,通过 CAS 将state减 1,直到state == 0时触发doReleaseShared唤醒所有在await处阻塞的线程。
java
private static final class Sync extends AbstractQueuedSynchronizer {
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
// 当且仅当 state == 0 时,tryAcquireShared 返回 1(成功),否则返回 -1(阻塞排队)
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
// countDown 触发共享释放
protected boolean tryReleaseShared(int releases) {
for (;;) {
int c = getState();
if (c == 0) // state 已经是 0,无需再减
return false;
int nextc = c - 1;
if (compareAndSetState(c, nextc)) {
// 只有当 count 递减至 0 的那一次 CAS 操作,才返回 true
// 这将触发 AQS 的 doReleaseShared,向后广播唤醒所有在 await() 上挂起的线程
return nextc == 0;
}
}
}
}
3.4 ReentrantReadWriteLock (读写锁)
ReentrantReadWriteLock 在单套 AQS 架构下实现了读写分离锁(读读共享,读写互斥,写写互斥)。
其核心设计在于将 32 位的整型 state 按位切分为高低两部分:
-
高 16 位 :表示读锁(Shared)持有计数值。
-
低 16 位 :表示写锁(Exclusive)重入计数值。
32位 state 内部结构:
+---------------------------------+---------------------------------+
| 高 16 位:读锁共享计数 | 低 16 位:写锁独占重入计数 |
| (state >>> 16) | (state & 0x0000FFFF) |
+---------------------------------+---------------------------------+
状态位切割辅助源码
java
abstract static class Sync extends AbstractQueuedSynchronizer {
static final int SHARED_SHIFT = 16;
static final int SHARED_UNIT = (1 << SHARED_SHIFT); // 读锁计数递增单位:65536
static final int MAX_COUNT = (1 << SHARED_SHIFT) - 1; // 单种锁最大计数 65535
static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1; // 低 16 位掩码:0x0000FFFF
/** 获取读锁持有数(高 16 位) */
static int sharedCount(int c) { return c >>> SHARED_SHIFT; }
/** 获取写锁重入数(低 16 位) */
static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
}
写锁获取逻辑(独占模式):tryAcquire
java
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount(c); // 获取当前写锁重入数
if (c != 0) {
// (Note: if c != 0 and w == 0 then sharedCount(c) != 0)
// 1. 如果 c != 0 且 w == 0,说明存在读锁;读锁存在时禁止获取写锁(读写互斥)
// 2. 如果 w != 0 且持有写锁的不是当前线程,获取写锁失败(写写互斥)
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error("Maximum lock count exceeded");
// 当前线程再次获取写锁,累加低 16 位重入计数
setState(c + acquires);
return true;
}
// c == 0,锁完全空闲,检查公平策略(writerShouldBlock)并尝试 CAS 抢写锁
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}
读锁获取逻辑(共享模式):tryAcquireShared
java
protected final int tryAcquireShared(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
// 互斥判断:若写锁已被其他线程持有 (exclusiveCount(c) != 0 且 owner != current),获取读锁失败
// 注意:如果当前线程持有写锁,它依然可以获取读锁(这称为"锁降级")
if (exclusiveCount(c) != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount(c); // 获取读锁共享计数值
// 在无需阻塞(readerShouldBlock)、未超限且 CAS 增加高 16 位读锁计数成功的情况下获取读锁
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) { // 增加高 16 位:c + 2^16
if (r == 0) {
firstReader = current; // 记录首个获取读锁的线程优化性能
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
// 使用 ThreadLocalHoldCounter 记录每个线程独立的读锁重入次数
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1; // 成功获取共享锁
}
// CAS 竞争失败或需要排队阻塞时,进入全功能自旋重试(fullTryAcquireShared)
return fullTryAcquireShared(current);
}
锁降级与升级:
- 锁降级(Lock Downgrading) :允许线程持有写锁的同时获取读锁,随后释放写锁,使锁降级为读锁。AQS 源码在
tryAcquireShared中对exclusiveCount(c) != 0 && getExclusiveOwnerThread() == current予以放行支持。- 锁升级(Lock Upgrading) :在持有读锁的同时直接尝试获取写锁。AQS 不支持锁升级,因为多线程并发尝试锁升级会导致死锁(所有线程都在等待其他线程释放读锁)。
4. J.U.C 组件在 AQS 钩子中的实现映射总结
| J.U.C 组件 | AQS 模式 | state 含义 |
tryAcquire / tryAcquireShared 行为 |
tryRelease / tryReleaseShared 行为 |
|---|---|---|---|---|
| ReentrantLock | 独占 | 锁重入次数 | CAS 抢锁或比对 owner 递增 state |
递减 state,降为 0 时清空 owner |
| Semaphore | 共享 | 剩余 Permits 数 | CAS 扣减 state( s t a t e ≥ a c q u i r e s state \ge acquires state≥acquires) |
CAS 累加 state |
| CountDownLatch | 共享 | 倒计数 Count | 检查 state == 0(是则通过,否则阻塞) |
CAS 递减 state,减至 0 时返回 true 触发扩散唤醒 |
| ReentrantReadWriteLock (写锁) | 独占 | 低 16 位写锁重入数 | 检查无读锁且非他人持写锁,累加低 16 位 | 递减低 16 位,减至 0 时清空 owner |
| ReentrantReadWriteLock (读锁) | 共享 | 高 16 位读锁总持有数 | 检查无他人持写锁,CAS 增加高 16 位 (+65536) |
CAS 递减高 16 位 (-65536) |
5. 内存模型与底层物理原语支持
AQS 之所以能在硬件层面提供极高的吞吐量与极低开销,依赖于 JVM 与底层操作系统的三项支撑:
+------------------------------------------------------------------+
| AQS (模板方法 / 双向 CLH 队列) |
+------------------------------------------------------------------+
|
+--------------------------+--------------------------+
| | |
v v v
+---------------+ +-------------------+ +-------------------+
| Volatile 语义 | | Unsafe CAS 原语 | | LockSupport 挂起 |
| StoreLoad 屏障 | | cmpxchg 指令 | | park / unpark |
+---------------+ +-------------------+ +-------------------+
- Volatile 语义与内存屏障(Memory Barriers)
state以及 Node 的prev、next、waitStatus均为volatile变量。- 对
state的读写通过 Java 内存模型(JMM)强制插入屏障(如StoreLoad),防止指令重排序,保证跨 CPU 核心的数据总线缓存一致性(MESI 协议)。
- Unsafe CAS 指令(Compare-And-Swap)
- AQS 的入队、出队、状态修改均基于
Unsafe.compareAndSwapInt/compareAndSwapObject实现。 - 在 x86 架构下,底层通过编译为带有
lock cmpxchg前缀的汇编指令,在硬件总线/缓存行层面保证原子性。
- LockSupport 与操作系统线程调度
LockSupport.park()和unpark()底层调用Unsafe原语。- 在 Linux 平台下,其底层对应于 POSIX 线程库的
pthread_mutex与pthread_cond(或 Linux 内核原语futex:Fast Userspace Mutex),使线程在无法抢到锁时直接进入 Kernel 态阻塞,避免无休止的 CPU 消耗(Spin-Lock 开销)。