AQS源码解析:深入剖析Java并发核心框架

87 阅读10分钟

AQS源码解析:深入剖析Java并发核心框架

本文将深入分析Java并发包中的AbstractQueuedSynchronizer(AQS),这是Java并发工具(如ReentrantLockCountDownLatch等)的核心框架。我们将从全局视角出发,拆解AQS的源码脉络,分析其核心机制,包括独占模式、共享模式和条件队列的实现逻辑。随后,通过模拟面试场景,层层提问,检验对AQS的理解。文章结构清晰,先提供鸟瞰图,再细化分析,最后以面试形式加深理解。


一、AQS全局鸟瞰

AbstractQueuedSynchronizer(AQS)是Java并发包(java.util.concurrent.locks)中的核心抽象类,提供了基于FIFO等待队列的同步器框架,用于实现锁、信号量、事件等并发工具。AQS通过一个int类型的状态变量(state)和CLH队列管理线程的同步行为,支持独占模式和共享模式。

1.1 核心组件与结构

AQS的实现依赖以下关键部分:

  • 状态变量(state)volatile int类型,表示同步状态,含义由子类定义。例如:

    • ReentrantLock中,state == 0表示锁未被占用,state > 0表示锁被持有,数值表示重入次数。
    • CountDownLatch中,state表示剩余的计数。
  • CLH队列:基于Craig, Landin, and Hagersten锁的变体,维护等待线程的FIFO队列:

    • 队列由Node节点组成,每个节点代表一个等待线程。
    • 使用headtail指针管理队列,节点通过prevnext双向链接。
  • 独占模式与共享模式

    • 独占模式:一次只有一个线程可以获取同步器(如ReentrantLock)。
    • 共享模式:多个线程可以同时获取同步器(如Semaphore)。
  • 条件队列:通过ConditionObject支持条件等待(await/signal),类似于Objectwait/notify

  • 核心方法

    • 获取acquire(独占)、acquireShared(共享)。
    • 释放release(独占)、releaseShared(共享)。
    • 子类实现tryAcquiretryReleasetryAcquireSharedtryReleaseSharedisHeldExclusively

1.2 工作原理概览

AQS通过state和CLH队列管理线程的同步:

  1. 获取同步器

    • 线程调用acquireacquireShared,尝试通过tryAcquiretryAcquireShared更新state
    • 若失败,线程进入CLH队列,阻塞等待。
  2. 释放同步器

    • 线程调用releasereleaseShared,通过tryReleasetryReleaseShared更新state
    • 若释放成功,唤醒队列中的下一个线程。
  3. 条件等待

    • 线程在条件队列中等待(await),通过signal转移到CLH队列重新竞争。

1.3 设计理念

  • 模板方法模式:AQS定义了通用的队列和阻塞逻辑,子类通过实现try*方法定制状态管理。
  • 非公平性默认:AQS允许“插队”(barging),新线程可能优先获取同步器,子类可通过hasQueuedPredecessors实现公平性。
  • 高性能:使用CAS(compareAndSetState)和LockSupportpark/unpark)实现高效的线程管理。

二、AQS源码拆解

以下按照逻辑脉络拆解AQS的核心源码,重点分析其实现细节。

2.1 类结构与核心字段

public abstract class AbstractQueuedSynchronizer
    extends AbstractOwnableSynchronizer
    implements java.io.Serializable {
    private transient volatile Node head;
    private transient volatile Node tail;
    private volatile int state;
}
  • state:同步状态,volatile保证可见性,通过getStatesetStatecompareAndSetState操作。
  • headtail:CLH队列的头尾指针,volatile确保线程安全。
  • 继承AbstractOwnableSynchronizer:提供exclusiveOwnerThread记录独占线程。

2.2 Node类与CLH队列

abstract static class Node {
    volatile Node prev;
    volatile Node next;
    Thread waiter;
    volatile int status;
}
static final class ExclusiveNode extends Node { }
static final class SharedNode extends Node { }
static final class ConditionNode extends Node implements ForkJoinPool.ManagedBlocker { ... }
  • Node结构

    • prevnext:双向链接,构成CLH队列。
    • waiter:记录等待线程。
    • status:节点状态,包括WAITING(等待)、CANCELLED(取消)、COND(条件等待)。
  • 子类

    • ExclusiveNode:独占模式节点。
    • SharedNode:共享模式节点。
    • ConditionNode:条件队列节点,支持await/signal
  • 队列操作

    • enqueue:通过CAS将节点加入tail
    • signalNext:唤醒队列中的下一个节点。

2.3 核心获取逻辑

public final void acquire(int arg) {
    if (!tryAcquire(arg))
        acquire(null, arg, false, false, false, 0L);
}
  • acquire:独占模式获取,调用子类的tryAcquire,失败则进入acquire方法。
  • 核心方法acquire(Node, int, boolean, boolean, boolean, long)
final int acquire(Node node, int arg, boolean shared,
                  boolean interruptible, boolean timed, long time) {
    Thread current = Thread.currentThread();
    byte spins = 0, postSpins = 0;
    boolean interrupted = false, first = false;
    Node pred = null;

    for (;;) {
        if (!first && (pred = (node == null) ? null : node.prev) != null &&
            !(first = (head == pred))) {
            if (pred.status < 0) {
                cleanQueue();
                continue;
            } else if (pred.prev == null) {
                Thread.onSpinWait();
                continue;
            }
        }
        if (first || pred == null) {
            boolean acquired;
            try {
                if (shared)
                    acquired = (tryAcquireShared(arg) >= 0);
                else
                    acquired = tryAcquire(arg);
            } catch (Throwable ex) {
                cancelAcquire(node, interrupted, false);
                throw ex;
            }
            if (acquired) {
                if (first) {
                    node.prev = null;
                    head = node;
                    pred.next = null;
                    node.waiter = null;
                    if (shared)
                        signalNextIfShared(node);
                    if (interrupted)
                        current.interrupt();
                }
                return 1;
            }
        }
        if (node == null) {
            if (shared)
                node = new SharedNode();
            else
                node = new ExclusiveNode();
        } else if (pred == null) {
            node.waiter = current;
            Node t = tail;
            node.setPrevRelaxed(t);
            if (t == null)
                tryInitializeHead();
            else if (!casTail(t, node))
                node.setPrevRelaxed(null);
            else
                t.next = node;
        } else if (first && spins != 0) {
            --spins;
            Thread.onSpinWait();
        } else if (node.status == 0) {
            node.status = WAITING;
        } else {
            long nanos;
            spins = postSpins = (byte)((postSpins << 1) | 1);
            if (!timed)
                LockSupport.park(this);
            else if ((nanos = time - System.nanoTime()) > 0L)
                LockSupport.parkNanos(this, nanos);
            else
                break;
            node.clearStatus();
            if ((interrupted |= Thread.interrupted()) && interruptible)
                break;
        }
    }
    return cancelAcquire(node, interrupted, interruptible);
}
  • 逻辑流程

    1. 检查是否为队列头部,若是则尝试tryAcquiretryAcquireShared
    2. 若获取失败,创建节点并尝试入队(casTail)。
    3. 入队后设置WAITING状态,调用LockSupport.park阻塞。
    4. 被唤醒后重试获取,处理中断或超时。
    5. 获取成功,更新head,清除节点引用。
  • 关键点

    • 使用CAS确保入队原子性。
    • 支持中断(interruptible)和超时(timed)。
    • 共享模式下,成功获取后唤醒后续共享节点(signalNextIfShared)。

2.4 核心释放逻辑

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        signalNext(head);
        return true;
    }
    return false;
}
  • release:独占模式释放,调用子类的tryRelease,成功则唤醒下一个节点(signalNext)。
  • signalNext
private static void signalNext(Node h) {
    Node s;
    if (h != null && (s = h.next) != null && s.status != 0) {
        s.getAndUnsetStatus(WAITING);
        LockSupport.unpark(s.waiter);
    }
}
  • 清除WAITING状态,调用LockSupport.unpark唤醒线程。

2.5 条件队列(ConditionObject)

public class ConditionObject implements Condition, java.io.Serializable {
    private transient ConditionNode firstWaiter;
    private transient ConditionNode lastWaiter;

    public final void signal() {
        ConditionNode first = firstWaiter;
        if (!isHeldExclusively())
            throw new IllegalMonitorStateException();
        if (first != null)
            doSignal(first, false);
    }

    public final void await() throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        ConditionNode node = new ConditionNode();
        int savedState = enableWait(node);
        LockSupport.setCurrentBlocker(this);
        boolean interrupted = false, cancelled = false, rejected = false;
        while (!canReacquire(node)) {
            if (interrupted |= Thread.interrupted()) {
                if (cancelled = (node.getAndUnsetStatus(COND) & COND) != 0)
                    break;
            } else if ((node.status & COND) != 0) {
                try {
                    if (rejected)
                        node.block();
                    else
                        ForkJoinPool.managedBlock(node);
                } catch (RejectedExecutionException ex) {
                    rejected = true;
                } catch (InterruptedException ie) {
                    interrupted = true;
                }
            } else
                Thread.onSpinWait();
        }
        LockSupport.setCurrentBlocker(null);
        node.clearStatus();
        acquire(node, savedState, false, false, false, 0L);
        if (interrupted) {
            if (cancelled) {
                unlinkCancelledWaiters(node);
                throw new InterruptedException();
            }
            Thread.currentThread().interrupt();
        }
    }
}
  • 结构ConditionNode组成单向链表(nextWaiter),维护条件等待队列。

  • signal:将firstWaiter转移到CLH队列(enqueue),唤醒线程。

  • await

    1. 创建ConditionNode,加入条件队列。
    2. 释放锁(release),保存state
    3. 阻塞(LockSupport.parkForkJoinPool.managedBlock)。
    4. signal唤醒后,转移到CLH队列,重新acquire锁。
  • 关键点

    • 条件队列与CLH队列分离,仅在独占模式下使用。
    • 支持中断和超时处理。

2.6 队列管理

  • 入队(enqueue)
final void enqueue(Node node) {
    if (node != null) {
        for (;;) {
            Node t = tail;
            node.setPrevRelaxed(t);
            if (t == null)
                tryInitializeHead();
            else if (casTail(t, node)) {
                t.next = node;
                if (t.status < 0)
                    LockSupport.unpark(node.waiter);
                break;
            }
        }
    }
}
  • 使用CAS更新tail,确保原子性。
  • 懒初始化headtail,减少无竞争时的开销。
  • 清理(cleanQueue)
private void cleanQueue() {
    for (;;) {
        for (Node q = tail, s = null, p, n;;) {
            if (q == null || (p = q.prev) == null)
                return;
            if (s == null ? tail != q : (s.prev != q || s.status < 0))
                break;
            if (q.status < 0) {
                if ((s == null ? casTail(q, p) : s.casPrev(q, p)) &&
                    q.prev == p) {
                    p.casNext(q, s);
                    if (p.prev == null)
                        signalNext(p);
                }
                break;
            }
            if ((n = p.next) != q) {
                if (n != null && q.prev == p) {
                    p.casNext(n, q);
                    if (p.prev == null)
                        signalNext(p);
                }
                break;
            }
            s = q;
            q = q.prev;
        }
    }
}
  • 移除取消节点(status == CANCELLED),修复队列链接。

三、AQS生命周期分析

以下分析AQS在独占模式和共享模式下的生命周期,以ReentrantLock(独占)和Semaphore(共享)为例。

3.1 独占模式(以ReentrantLock为例)

  1. 获取锁

    • 线程调用lock(),触发tryAcquire(检查state == 0,CAS设置为1)。
    • 若成功,设置exclusiveOwnerThread,返回。
    • 若失败,创建ExclusiveNode,入队,调用LockSupport.park阻塞。
  2. 释放锁

    • 线程调用unlock(),触发tryReleasestate--,若state == 0,清除exclusiveOwnerThread)。
    • 若释放成功,调用signalNext唤醒队列头部线程。
  3. 条件等待

    • 线程调用Condition.await(),创建ConditionNode,加入条件队列,释放锁。
    • 调用signal(),将ConditionNode转移到CLH队列,线程重新竞争锁。

3.2 共享模式(以Semaphore为例)

  1. 获取许可

    • 线程调用acquire(),触发tryAcquireShared(检查state >= permits,CAS减少state)。
    • 若成功,返回。
    • 若失败,创建SharedNode,入队,阻塞。
  2. 释放许可

    • 线程调用release(),触发tryReleaseShared(CAS增加state)。
    • 若成功,调用signalNextIfShared,唤醒后续共享节点。
  3. 传播机制

    • 共享模式下,一个线程获取成功后,通知后续共享节点尝试获取,允许多线程同时通过。

3.3 关键点

  • 独占模式:严格互斥,队列头部线程独占同步器。
  • 共享模式:允许多线程同时获取,传播机制提高吞吐量。
  • 条件队列:仅在独占模式下使用,与CLH队列动态切换。

四、模拟面试:层层拷打

以下模拟面试官对AQS的提问,从基础到深入,检验理解深度。

4.1 基础问题

Q1:AQS是什么?它的核心作用是什么?

  • A:AQS(AbstractQueuedSynchronizer)是Java并发包中的同步器框架,基于state和CLH队列实现阻塞锁和同步工具(如ReentrantLockSemaphore)。它通过模板方法模式,定义通用逻辑,子类实现具体状态管理。

Q2:AQS支持哪些模式?如何区分独占和共享模式?

  • A

    • 独占模式:一次只有一个线程获取(如ReentrantLock),通过tryAcquire/tryRelease
    • 共享模式:允许多线程获取(如Semaphore),通过tryAcquireShared/tryReleaseShared
    • 区分在于tryAcquireShared返回int(负数失败,0成功但不传播,正数成功并传播)。

4.2 深入问题

Q3:AQS的CLH队列如何实现线程阻塞和唤醒?

  • A

    • 入队:线程通过casTailNode加入队列尾部,设置WAITING状态。
    • 阻塞:调用LockSupport.park,线程挂起。
    • 唤醒:释放时调用signalNext,通过LockSupport.unpark唤醒队列头部线程。
    • 使用CAS和volatile确保线程安全。

Q4:AQS如何实现公平性?

  • A

    • 默认非公平,允许新线程插队(barging)。
    • 公平性通过hasQueuedPredecessors实现,子类在tryAcquire中检查队列,若有前驱线程,返回false,防止插队。

4.3 刁钻问题

Q5:如果队列中有多个线程等待,释放锁后会发生什么?

  • A

    • 独占模式:tryRelease成功后,signalNext唤醒队列头部线程,尝试tryAcquire
    • 共享模式:tryReleaseShared成功后,signalNextIfShared唤醒共享节点,多个线程可能同时获取。
    • 非公平模式下,新线程可能插队抢占。

Q6:AQS的state溢出怎么办?

  • A

    • stateint,最大值为Integer.MAX_VALUE
    • 子类需在tryAcquire/tryRelease中检查溢出,抛出IllegalMonitorStateException或自定义错误。
    • 实际场景需合理设计state语义,避免溢出。

4.4 场景问题

Q7:调试时发现IllegalMonitorStateException,可能原因是什么?

  • A

    • 释放锁时,当前线程不是持有者(tryReleaseisHeldExclusively失败)。
    • 调用Condition.signal/await时,未持有锁。
    • 解决:确保acquirerelease成对,检查线程逻辑。

Q8:如何用AQS实现一个简单的互斥锁?

  • A
class Mutex implements Lock {
    private static class Sync extends AbstractQueuedSynchronizer {
        protected boolean tryAcquire(int arg) {
            if (compareAndSetState(0, 1)) {
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean tryRelease(int arg) {
            if (getState() == 0)
                throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }

        protected boolean isHeldExclusively() {
            return getState() == 1 && getExclusiveOwnerThread() == Thread.currentThread();
        }

        public Condition newCondition() {
            return new ConditionObject();
        }
    }

    private final Sync sync = new Sync();

    public void lock() { sync.acquire(1); }
    public void unlock() { sync.release(1); }
    public Condition newCondition() { return sync.newCondition(); }
    public void lockInterruptibly() throws InterruptedException { sync.acquireInterruptibly(1); }
    public boolean tryLock() { return sync.tryAcquire(1); }
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(1, unit.toNanos(time));
    }
}
  • tryAcquire:CAS设置state为1,记录持有线程。
  • tryRelease:清空state和持有线程。
  • 使用AQS的acquire/release实现锁逻辑。

五、总结

AbstractQueuedSynchronizer是Java并发编程的基石,通过state和CLH队列实现了高效的同步机制。核心要点包括:

  • 框架设计:模板方法模式,子类定制try*方法。
  • 队列管理:CLH队列支持独占和共享模式,CAS和LockSupport确保高性能。
  • 条件队列:支持灵活的等待/通知机制。
  • 灵活性:支持公平/非公平、中断、超时等多种场景。
    通过源码拆解和模拟面试,我们深入理解了AQS的实现原理和应用场景,为掌握Java并发工具奠定了基础。

希望这篇分析能帮助你彻底理解AQS,在并发编程中游刃有余!