JUC组件逐层剥离与深度剖析

JUC组件逐层剥离与深度剖析

  • 前言
  • JUC组件逐层剥离与深度剖析
    • [1. AQS 核心设计思想与底层数据结构](#1. AQS 核心设计思想与底层数据结构)
      • [1.1 同步状态量 `state`](#1.1 同步状态量 state)
      • [1.2 CLH 队列节点 `Node` 内部类](#1.2 CLH 队列节点 Node 内部类)
    • [2. OpenJDK 8 AQS 核心源码逐行剖析](#2. OpenJDK 8 AQS 核心源码逐行剖析)
      • [2.1 独占模式获取锁(Acquire)](#2.1 独占模式获取锁(Acquire))
      • [2.2 独占模式释放锁(Release)](#2.2 独占模式释放锁(Release))
      • [2.3 共享模式获取与传播机制(Shared Mode)](#2.3 共享模式获取与传播机制(Shared Mode))
        • [传播性核心实现:`setHeadAndPropagate` 与 JDK-6801020 Bug 修复](#传播性核心实现:setHeadAndPropagate 与 JDK-6801020 Bug 修复)
      • [2.4 条件变量:`ConditionObject` 条件队列](#2.4 条件变量:ConditionObject 条件队列)
    • [3. AQS 如何支撑四种典型 J.U.C 组件](#3. AQS 如何支撑四种典型 J.U.C 组件)
    • [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();
}
入队操作:addWaiterenq
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 成功后。因此,在任何时刻从 tailprev 遍历队列是绝对安全的,而从 headnext 遍历可能遇到 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)

共享模式(如 CountDownLatchSemaphoreReentrantReadWriteLock 的读锁)允许多个线程同时持有资源。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 实现的高效等待/通知机制。它维持了一个单向的条件等待队列 (以 firstWaiterlastWaiter 为头尾)。

复制代码
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     |
+---------------+        +-------------------+      +-------------------+
  1. Volatile 语义与内存屏障(Memory Barriers)
  • state 以及 Node 的 prevnextwaitStatus 均为 volatile 变量。
  • state 的读写通过 Java 内存模型(JMM)强制插入屏障(如 StoreLoad),防止指令重排序,保证跨 CPU 核心的数据总线缓存一致性(MESI 协议)。
  1. Unsafe CAS 指令(Compare-And-Swap)
  • AQS 的入队、出队、状态修改均基于 Unsafe.compareAndSwapInt / compareAndSwapObject 实现。
  • 在 x86 架构下,底层通过编译为带有 lock cmpxchg 前缀的汇编指令,在硬件总线/缓存行层面保证原子性。
  1. LockSupport 与操作系统线程调度
  • LockSupport.park()unpark() 底层调用 Unsafe 原语。
  • 在 Linux 平台下,其底层对应于 POSIX 线程库的 pthread_mutexpthread_cond(或 Linux 内核原语 futex:Fast Userspace Mutex),使线程在无法抢到锁时直接进入 Kernel 态阻塞,避免无休止的 CPU 消耗(Spin-Lock 开销)。
相关推荐
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题 第210题】【10_网络协议篇】第1题:说说 TCP/IP 网络五层模型
java·网络协议·tcp/ip·计算机网络·网络五层模型
大黄说说1 小时前
EF Core 避坑指南:查询慢、循环查询、并发更新问题如何解决
java·服务器·数据库
城管不管1 小时前
重生——第五次面试2026.8.1一面
java·数据库·后端·ai·面试·职场和发展·agent
DarLing丶张皇1 小时前
【源码】JeecgBoot导出Excel模板
java·spring boot
ocean'1 小时前
防火墙策略路由
linux·服务器·数据库
三言老师1 小时前
awk条件筛选日志内容实操
linux·运维·服务器·centos
223糖1 小时前
Idea,pycharm2026激活保姆级教程
java·ide·intellij-idea
hhzz1 小时前
机器学习-算法模型系列文章:07-SVM 一巴掌拍出来的超平面:SVM核函数选错,再干净的数据也救不了你
算法·机器学习·支持向量机
songgz2 小时前
zVM统一OS与DB
jvm·数据库·vm