当前位置: 首页 > news >正文

JUC之AQS

文章目录

  • 一、AQS核心概念与设计哲学
    • 1.1 AQS是什么?
    • 1.2 AQS的核心数据结构
  • 二、AQS核心原理深度解析
    • 2.1 同步状态【state】管理
    • 2.2 CLH队列的工作原理
    • 2.3 模板方法模式
  • 三、AQS两种同步模式
    • 3.1 独占模式(Exclusive)
      • 3.1.1 独占模式获取资源
      • 3.1.2 关键辅助方法解析
      • 3.1.3 独占模式释放资源
    • 3.2 共享模式(Shared)
      • 3.2.1 共享模式获取资源
      • 3.2.2 共享模式释放资源
  • 四、基于AQS的实现示例
    • 4.1 自定义互斥锁
    • 4.2 CustomMutex使用示例
    • 4.3 不可重入互斥锁(Mutex)
    • 4.4 二元信号量(BinarySemaphore)
    • 4.5 自定义高级同步器
  • 五、AQS在JUC中的实际应用
    • 5.1 ReentrantLock 中的 AQS 应用
    • 5.2 CountDownLatch 中的 AQS 应用
  • 六、AQS高级特性
      • 6.1 条件变量(Condition)的实现
    • 6.2 Condition示例
  • 七、AQS性能优化与最佳实践
    • 7.1 最佳实践总结
    • 7.2 使用经验
      • ❌ 避免陷阱
  • 八、总结AQS

一、AQS核心概念与设计哲学

1.1 AQS是什么?

AbstractQueuedSynchronizer(AQS)是 Java 并发包 (java.util.concurrent.locks) 中的核心框架,用于构建锁和其他同步器的基础。它提供了一个基于 FIFO 等待队列的同步器框架,JUC 中的大多数同步工具如 ReentrantLock、Semaphore、CountDownLatch 都是基于 AQS 构建的。

核心设计思想

  • 状态管理:使用一个 volatile int 类型的 state 变量表示同步状态
  • 队列管理:通过内置的 FIFO 队列管理获取同步状态失败的线程
  • 模板方法:通过模板方法模式,子类通过继承并实现指定方法管理同步状态
基于AQS的实现
AQS 核心架构
ReentrantLock
Semaphore
CountDownLatch
CyclicBarrier
ReentrantReadWriteLock
AQS
volatile int state
同步状态
CLH队列
FIFO线程等待队列
独占模式
如ReentrantLock
共享模式
如Semaphore

1.2 AQS的核心数据结构

// AQS 简化核心结构
public abstract class AbstractQueuedSynchronizer {// 同步状态,volatile保证可见性private volatile int state;// CLH队列头节点private transient volatile Node head;// CLH队列尾节点private transient volatile Node tail;// CLH队列节点定义static final class Node {// 节点模式:共享、独占static final Node SHARED = new Node();static final Node EXCLUSIVE = null;// 等待状态volatile int waitStatus;// 前驱和后继节点volatile Node prev;volatile Node next;// 节点关联的线程volatile Thread thread;}
}

二、AQS核心原理深度解析

组件说明
stateint 类型,表示同步状态。由 volatile 修饰,保证可见性。
head / tail指向同步队列的头节点和尾节点。
Node队列中的节点,封装等待线程及其等待状态。
CLH 队列变体AQS 使用的是 CLH 锁队列的变体,用于管理等待线程。

2.1 同步状态【state】管理

AQS 使用一个整型的 state 来表示同步状态,不同的子类对其有不同的解释:

  • ReentrantLock:state 表示锁的重入次数(0表示未锁定,>0表示重入次数)
  • Semaphore:state 表示可用的许可数量
  • CountDownLatch:state 表示计数器值
  • ReentrantReadWriteLock:高16位表示读锁数量,低16位表示写锁重入次数

2.2 CLH队列的工作原理

AQS 使用 CLH 变体的双向队列来管理等待线程。CLH 队列是一个 FIFO 队列,具有以下特性:

  1. 公平性:保证等待时间最长的线程最先获取锁
  2. 高效性:通过前驱节点的状态来减少不必要的线程唤醒
  3. 可扩展性:支持大量线程的排队管理

node的节点结构

static final class Node {volatile int waitStatus;     // 等待状态volatile Node prev;          // 前驱节点volatile Node next;          // 后继节点volatile Thread thread;      // 关联的线程Node nextWaiter;             // 下一个等待节点(用于条件队列)
}

等待状态的值

状态含义
SIGNAL-1当前节点的后继节点已被阻塞,需要当前节点释放时唤醒它。
CANCELLED1线程已取消(如中断或超时)。
CONDITION-2节点在条件队列中。
PROPAGATE-3共享模式下,释放操作应传播到后续节点。

同步队列结构

AQS 同步队列
Node: Thread A
head
Node: Thread B
Node: Thread C
tail

说明:队列为双向链表,head 是虚拟头节点(不关联线程),tail 指向最后一个真实节点。

// CLH队列操作的核心方法
private Node addWaiter(Node mode) {// 创建新节点,包含当前线程和模式(独占/共享)Node node = new Node(Thread.currentThread(), mode);// 快速尝试添加到队列尾部Node pred = tail;if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}// 如果快速添加失败,使用完整入队流程enq(node);return node;
}private Node enq(final Node node) {for (;;) { // 自旋直到成功入队Node t = tail;if (t == null) { // 队列为空,需要初始化if (compareAndSetHead(new Node())) // 设置哑节点tail = head;} else {node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}
}

2.3 模板方法模式

AQS 使用模板方法模式,子类需要实现以下关键方法:

public abstract class AbstractQueuedSynchronizer {// 尝试获取独占锁,需要子类实现protected boolean tryAcquire(int arg) {throw new UnsupportedOperationException();}// 尝试释放独占锁,需要子类实现protected boolean tryRelease(int arg) {throw new UnsupportedOperationException();}// 尝试获取共享锁,需要子类实现protected int tryAcquireShared(int arg) {throw new UnsupportedOperationException();}// 尝试释放共享锁,需要子类实现protected boolean tryReleaseShared(int arg) {throw new UnsupportedOperationException();}// 判断是否独占模式,需要子类实现protected boolean isHeldExclusively() {throw new UnsupportedOperationException();}
}

三、AQS两种同步模式

3.1 独占模式(Exclusive)

一次只有一个线程可以获取同步状态,如 ReentrantLock。

// 独占模式获取同步状态的核心流程
public final void acquire(int arg) {if (!tryAcquire(arg) && // 首先尝试获取acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) // 失败则加入队列selfInterrupt(); // 如果等待过程中被中断,重新设置中断标志
}// 独占模式获取同步状态的详细流程
public final void acquire(int arg) {if (!tryAcquire(arg)) { // 尝试获取锁失败Node node = addWaiter(Node.EXCLUSIVE); // 创建独占模式节点并入队boolean interrupted = false;for (;;) { // 自旋final Node p = node.predecessor(); // 获取前驱节点if (p == head && tryAcquire(arg)) { // 如果前驱是头节点且获取成功setHead(node); // 将自己设为头节点p.next = null; // 帮助GCreturn;}if (shouldParkAfterFailedAcquire(p, node) && // 检查是否需要parkparkAndCheckInterrupt()) // park并检查中断interrupted = true;}}
}

独占模式获取锁流程

在这里插入图片描述

独占模式释放锁

成功
失败
线程调用 release(int arg)
tryRelease(arg)
unparkSuccessor(head)
获取 head 的 next 节点
LockSupport.unpark(next.thread)
唤醒线程继续执行 acquireQueued
释放失败

特点:同一时间只有一个线程能获取同步资源(如ReentrantLock的独占锁)。

核心流程:线程获取资源→成功则执行→失败则加入等待队列并阻塞→资源释放后唤醒队列中的下一个线程。

3.1.1 独占模式获取资源

acquire (int arg)

acquire是 AQS 提供的模板方法,子类无需重写,直接调用即可。流程如下:

  1. 调用子类重写的tryAcquire(arg)

    尝试获取资源:

    • 成功:直接返回,线程继续执行;
    • 失败:执行后续步骤。
  2. 调用addWaiter(Node.EXCLUSIVE)创建独占模式的Node节点,并加入队列尾部;

  3. 调用acquireQueued(node, arg)让节点在队列中自旋等待,直到获取资源或被取消。

简化后的核心代码:

public final void acquire(int arg) {// 1. 尝试获取资源,失败则加入队列并自旋等待if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg)) {// 若自旋过程中被中断,标记当前线程需要中断selfInterrupt();}
}

3.1.2 关键辅助方法解析

  1. tryAcquire(int arg)

抽象方法,由子类实现,定义 “如何获取资源” 的逻辑。AQS 中默认抛出UnsupportedOperationException,子类必须重写(如ReentrantLock)。示例(非公平锁的tryAcquire实现)

protected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();// 1. 若状态为0(无锁),尝试CAS获取锁if (c == 0) {if (compareAndSetState(0, acquires)) {// 2. 标记当前线程为锁持有者setExclusiveOwnerThread(current);return true;}}// 3. 若当前线程已持有锁(重入),增加state计数else if (current == getExclusiveOwnerThread()) {int nextc = c + acquires;if (nextc < 0) // 防止溢出throw new Error("Maximum lock count exceeded");setState(nextc);return true;}// 4. 其他情况,获取失败return false;
}
  1. addWaiter(Node mode)

创建节点并加入队列尾部(CAS 保证线程安全),流程如下:

private Node addWaiter(Node mode) {// 1. 创建当前线程的Node节点(mode为EXCLUSIVE或SHARED)Node node = new Node(Thread.currentThread(), mode);Node pred = tail;// 2. 若队列不为空,直接尝试CAS将节点加入尾部if (pred != null) {node.prev = pred;if (compareAndSetTail(pred, node)) {pred.next = node;return node;}}// 3. 队列为空或CAS失败,通过enq方法自旋加入队列enq(node);return node;
}// 自旋CAS加入队列,确保节点被成功添加
private Node enq(final Node node) {for (;;) {Node t = tail;if (t == null) { // 队列空,初始化头节点(虚拟节点)if (compareAndSetHead(new Node()))tail = head;} else { // 队列非空,CAS添加到尾部node.prev = t;if (compareAndSetTail(t, node)) {t.next = node;return t;}}}
}
  1. acquireQueued(final Node node, int arg)

节点在队列中自旋等待,核心逻辑是 “前驱节点是头节点时,再次尝试获取资源;否则阻塞当前线程”,流程如下:

final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;// 自旋循环,直到获取资源或被取消for (;;) {final Node p = node.predecessor(); // 获取前驱节点// 1. 若前驱是头节点,尝试获取资源if (p == head && tryAcquire(arg)) {setHead(node); // 2. 获取成功,将当前节点设为头节点(原头节点被GC回收)p.next = null; // 3. 断开原头节点的引用,避免内存泄漏failed = false;return interrupted; // 4. 返回是否被中断过}// 5. 若获取失败,判断是否需要阻塞当前线程if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt()) {// 6. 若线程被中断,标记interrupted为trueinterrupted = true;}}} finally {if (failed) {// 7. 若获取资源失败且发生异常,取消当前节点cancelAcquire(node);}}
}

其中,shouldParkAfterFailedAcquire方法用于设置前驱节点的状态为SIGNAL,确保后续能被唤醒;parkAndCheckInterrupt方法通过LockSupport.park(this)阻塞当前线程,并返回是否被中断。

3.1.3 独占模式释放资源

release (int arg)

release也是模板方法,用于释放资源并唤醒队列中的下一个线程,流程如下:

  1. 调用子类重写的tryRelease(arg)

    尝试释放资源:

    • 成功:执行后续步骤;
    • 失败:直接返回false
  2. 唤醒队列中的下一个节点(若存在)。

简化后的核心代码

public final boolean release(int arg) {// 1. 尝试释放资源if (tryRelease(arg)) {Node h = head;// 2. 若头节点不为空且状态不是初始状态,唤醒下一个节点if (h != null && h.waitStatus != 0) {unparkSuccessor(h);}return true;}return false;
}

tryRelease 示例(ReentrantLock 实现)

protected final boolean tryRelease(int releases) {int c = getState() - releases;// 1. 只有锁持有者才能释放锁if (Thread.currentThread() != getExclusiveOwnerThread())throw new IllegalMonitorStateException();boolean free = false;// 2. 若state减为0,说明锁已完全释放if (c == 0) {free = true;setExclusiveOwnerThread(null); // 清除锁持有者}setState(c); // 更新状态return free;
}

unparkSuccessor 方法:唤醒头节点的后继节点(跳过CANCELLED状态的节点),核心逻辑是通过LockSupport.unpark(s.thread)唤醒线程。

3.2 共享模式(Shared)

特点:同一时间多个线程可同时获取同步资源(如SemaphoreCountDownLatch)。

核心流程与独占模式类似,但差异在于:

  • 共享模式下,线程获取资源成功后,会继续唤醒后续等待的共享线程(如CountDownLatchcountDown后,所有等待线程被唤醒);
  • 核心方法为acquireShared(int arg)(获取资源)和releaseShared(int arg)(释放资源)。
// 共享模式获取同步状态
public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0) // 尝试获取doAcquireShared(arg); // 失败则加入队列
}// 共享模式获取的详细实现
private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED); // 创建共享模式节点boolean interrupted = false;try {for (;;) { // 自旋final Node p = node.predecessor(); // 前驱节点if (p == head) {int r = tryAcquireShared(arg); // 尝试获取共享锁if (r >= 0) { // 获取成功setHeadAndPropagate(node, r); // 设置头节点并传播p.next = null; // 帮助GCreturn;}}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (interrupted)selfInterrupt();}
}
释放共享资源
获取共享资源
成功
tryReleaseShared(arg)
releaseShared(arg)
doReleaseShared()
唤醒 head.next
若为共享节点, 继续传播
tryAcquireShared(arg) >= 0
acquireShared(arg)
获取成功
doAcquireShared(arg)
addWaiter(SHARED)
自旋 + 尝试获取
获取成功?
setHeadAndPropagate()
传播唤醒后续共享节点

3.2.1 共享模式获取资源

acquireShared (int arg)

模板方法,流程如下:

  1. 调用子类重写的tryAcquireShared(arg)

    尝试获取资源:

    • 返回值>=0:获取成功,返回;
    • 返回值<0:获取失败,加入队列并阻塞。
  2. 若获取失败,调用doAcquireShared(arg)将节点加入队列并自旋等待。

简化后的核心代码

public final void acquireShared(int arg) {if (tryAcquireShared(arg) < 0) {doAcquireShared(arg); // 加入队列并自旋}
}

tryAcquireShared 示例(Semaphore 实现)

protected int tryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;// 1. 若可用许可不足,或CAS修改许可失败,继续自旋if (remaining < 0 ||compareAndSetState(available, remaining)) {return remaining; // 返回剩余许可(<0表示获取失败)}}
}

3.2.2 共享模式释放资源

releaseShared (int arg)

模板方法,流程如下:

  1. 调用子类重写的tryReleaseShared(arg)尝试释放资源(CAS 确保原子性);
  2. 若释放成功,唤醒队列中的后续共享节点。

简化后的核心代码

public final boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared(); // 唤醒后续节点return true;}return false;
}

tryReleaseShared 示例(Semaphore 实现)

protected final boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (next < current) // 防止溢出throw new Error("Maximum permit count exceeded");// CAS修改许可数量,成功则返回trueif (compareAndSetState(current, next))return true;}
}

四、基于AQS的实现示例

4.1 自定义互斥锁

package cn.tcmeta.aqs;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;/*** 基于AQS的自定义互斥锁实现* 演示AQS独占模式的基本用法*/
public class CustomMutex implements Lock {private final Sync sync = new Sync();// 自定义同步器,继承AQSprivate static class Sync extends AbstractQueuedSynchronizer {// 是否被占用@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}// 尝试获取锁@Overridepublic boolean tryAcquire(int acquires) {// 使用CAS操作尝试将state从0改为1if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread()); // 设置独占线程return true;}return false;}// 尝试释放锁@Overrideprotected boolean tryRelease(int releases) {if (getState() == 0) throw new IllegalMonitorStateException("锁未被当前线程持有");setExclusiveOwnerThread(null); // 清除独占线程setState(0); // 释放锁return true;}// 创建条件变量Condition newCondition() {return new ConditionObject();}}// Lock接口方法实现@Overridepublic void lock() { sync.acquire(1); }@Overridepublic boolean tryLock() { return sync.tryAcquire(1); }@Overridepublic void unlock() { sync.release(1); }@Overridepublic Condition newCondition() { return sync.newCondition(); }@Overridepublic void lockInterruptibly() throws InterruptedException {sync.acquireInterruptibly(1);}@Overridepublic boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}
}

4.2 CustomMutex使用示例

package cn.tcmeta.aqs;import java.util.concurrent.CountDownLatch;/*** AQS自定义锁使用示例*/
public class AQSExample {private final CustomMutex mutex = new CustomMutex();private int sharedResource = 0;public void demonstrateMutex() throws InterruptedException {// 创建多个线程竞争访问共享资源int threadCount = 5;CountDownLatch startLatch = new CountDownLatch(1);CountDownLatch endLatch = new CountDownLatch(threadCount);for (int i = 0; i < threadCount; i++) {final int threadId = i;new Thread(() -> {try {startLatch.await(); // 等待开始信号// 使用自定义互斥锁保护临界区mutex.lock();try {System.out.println("线程 " + threadId + " 获取锁,共享资源: " + sharedResource);sharedResource++;Thread.sleep(100); // 模拟工作} finally {mutex.unlock();}} catch (InterruptedException e) {Thread.currentThread().interrupt();} finally {endLatch.countDown();}}).start();}startLatch.countDown(); // 开始所有线程endLatch.await(); // 等待所有线程完成System.out.println("最终共享资源值: " + sharedResource);}static void main(String[] args) throws InterruptedException {AQSExample example = new AQSExample();example.demonstrateMutex();}
}

在这里插入图片描述

4.3 不可重入互斥锁(Mutex)

package cn.tcmeta.aqs;import java.util.concurrent.locks.AbstractQueuedSynchronizer;/*** 基于 AQS 实现的不可重入互斥锁* state = 0: 未加锁, state = 1: 已加锁*/
public class Mutex {private static class Sync extends AbstractQueuedSynchronizer {@Overrideprotected boolean tryAcquire(int acquires) {// 仅当 state 为 0 时,通过 CAS 获取锁return compareAndSetState(0, 1);}@Overrideprotected boolean tryRelease(int releases) {// 释放锁,设置 state 为 0setState(0);return true;}@Overrideprotected boolean isHeldExclusively() {return getState() == 1;}}private final Sync sync = new Sync();public void lock() {sync.acquire(1);}public void unlock() {sync.release(1);}public boolean tryLock() {return sync.tryAcquire(1);}
}

4.4 二元信号量(BinarySemaphore)

package cn.tcmeta.aqs;import java.util.concurrent.locks.AbstractQueuedSynchronizer;/*** 二元信号量:只有 0 或 1 个许可*/
public class BinarySemaphore {private static class Sync extends AbstractQueuedSynchronizer {Sync(int permits) {setState(permits); // 初始化许可数}@Overrideprotected int tryAcquireShared(int acquires) {for (;;) {int available = getState();int remaining = available - acquires;if (remaining < 0 || compareAndSetState(available, remaining)) {return remaining;}}}@Overrideprotected boolean tryReleaseShared(int releases) {for (;;) {int current = getState();int next = current + releases;if (compareAndSetState(current, next)) {return true;}}}}private final Sync sync;public BinarySemaphore(int permits) {if (permits < 0) throw new IllegalArgumentException();this.sync = new Sync(permits);}public void acquire() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public void release() {sync.releaseShared(1);}
}

4.5 自定义高级同步器

支持超时等待锁

/*** 支持超时等待的自定义锁* 演示AQS高级用法*/
public class TimeoutMutex implements Lock {private final Sync sync = new Sync();private static class Sync extends AbstractQueuedSynchronizer {@Overrideprotected boolean tryAcquire(int acquires) {if (compareAndSetState(0, 1)) {setExclusiveOwnerThread(Thread.currentThread());return true;}return false;}@Overrideprotected boolean tryRelease(int releases) {if (getState() == 0) throw new IllegalMonitorStateException();setExclusiveOwnerThread(null);setState(0);return true;}// 添加超时获取方法public boolean tryAcquireNanos(int acquires, long nanosTimeout) throws InterruptedException {return tryAcquire(acquires) || doAcquireNanos(acquires, nanosTimeout);}}@Overridepublic void lock() {sync.acquire(1);}@Overridepublic boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {return sync.tryAcquireNanos(1, unit.toNanos(timeout));}// 其他方法实现...
}

五、AQS在JUC中的实际应用

5.1 ReentrantLock 中的 AQS 应用

/*** ReentrantLock中AQS应用分析*/
public class ReentrantLock implements Lock {private final Sync sync;abstract static class Sync extends AbstractQueuedSynchronizer {// 非公平锁尝试获取final boolean nonfairTryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) { // 锁未被占用if (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;}@Overrideprotected 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);return free;}}// 非公平锁实现static final class NonfairSync extends Sync {@Overrideprotected final boolean tryAcquire(int acquires) {return nonfairTryAcquire(acquires);}}// 公平锁实现static final class FairSync extends Sync {@Overrideprotected final boolean tryAcquire(int acquires) {final Thread current = Thread.currentThread();int c = getState();if (c == 0) {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;}}
}

5.2 CountDownLatch 中的 AQS 应用

/*** CountDownLatch中AQS应用分析*/
public class CountDownLatch {private static final class Sync extends AbstractQueuedSynchronizer {Sync(int count) {setState(count); // 初始化计数器}int getCount() {return getState();}// 共享模式尝试获取@Overrideprotected int tryAcquireShared(int acquires) {return (getState() == 0) ? 1 : -1;}// 共享模式尝试释放@Overrideprotected boolean tryReleaseShared(int releases) {for (;;) { // 自旋直到成功int c = getState();if (c == 0) return false; // 已经为0,不需要释放int nextc = c - 1;if (compareAndSetState(c, nextc)) // CAS更新状态return nextc == 0; // 返回是否达到0}}}private final Sync sync;public CountDownLatch(int count) {if (count < 0) throw new IllegalArgumentException("count < 0");this.sync = new Sync(count);}public void await() throws InterruptedException {sync.acquireSharedInterruptibly(1);}public void countDown() {sync.releaseShared(1);}
}

六、AQS高级特性

6.1 条件变量(Condition)的实现

AQS 内部类ConditionObject实现了Condition接口,提供了类似Object.wait()/notify()的等待 / 通知机制,但更灵活(一个锁可关联多个条件队列)

  • ConditionObject 是 AQS 的内部类。
  • 每个 Condition 对应一个条件等待队列(单向链表)。
  • await():当前线程释放锁,加入条件队列,阻塞。
  • signal():将条件队列首节点转移到同步队列,等待获取锁。
线程调用 condition.await()
释放锁
加入条件队列
阻塞
signal()
转移节点到同步队列
线程被唤醒, 重新竞争锁

示例流程

  1. 线程获取锁后调用condition.await(),释放锁并进入条件队列;
  2. 其他线程调用condition.signal(),将条件队列的节点移至主队列;
  3. 主队列中的节点按规则竞争锁,获取锁后继续执行。

6.2 Condition示例

/*** AQS条件变量使用示例*/
public class ConditionExample {private final Lock lock = new ReentrantLock();private final Condition condition = lock.newCondition();private boolean conditionMet = false;public void awaitCondition() throws InterruptedException {lock.lock();try {while (!conditionMet) { // 条件检查,防止虚假唤醒System.out.println("条件未满足,线程进入等待...");condition.await(); // 释放锁并等待}// 条件满足,执行操作System.out.println("条件满足,继续执行...");} finally {lock.unlock();}}public void signalCondition() {lock.lock();try {conditionMet = true;System.out.println("条件已满足,唤醒等待线程...");condition.signal(); // 唤醒一个等待线程} finally {lock.unlock();}}public void signalAllCondition() {lock.lock();try {conditionMet = true;System.out.println("条件已满足,唤醒所有等待线程...");condition.signalAll(); // 唤醒所有等待线程} finally {lock.unlock();}}
}

七、AQS性能优化与最佳实践

7.1 最佳实践总结

  1. 正确实现模板方法
    • 确保 tryAcquire/tryRelease 等方法的线程安全性
    • 正确管理同步状态 (state)
  2. 合理选择同步模式
    • 独占模式:互斥访问资源(如锁)
    • 共享模式:多个线程可同时访问(如信号量)
  3. 避免常见陷阱
    • 正确处理中断
    • 避免死锁
    • 注意内存可见性
  4. 性能考虑
    • 减少不必要的 CAS 操作
    • 合理使用自旋等待
    • 考虑公平性 vs 吞吐量权衡

7.2 使用经验

✅ 正确使用

  • 优先使用标准同步器:如 ReentrantLock,避免重复造轮子。

  • 钩子方法保持轻量:避免在 tryAcquire 中执行复杂逻辑。

  • 正确处理中断:在 acquireQueued 中检查中断状态。

  • 避免死锁:确保 tryRelease 一定能成功释放

❌ 避免陷阱

  • 不要在持有锁时调用阻塞操作(如 I/O)。
  • 不要重写 AQS 的模板方法(如 acquire),只重写钩子。
  • 避免在 tryRelease 中抛出异常,否则队列无法唤醒

八、总结AQS

AQS 的成功源于其分层设计思想

  • 底层:基于 CAS + volatile + LockSupport 实现无锁并发
  • 中层:CLH 队列管理线程排队
  • 上层:模板方法 + 钩子方法 提供扩展点

一句话总结

  • AQS 是 Java 并发的“操作系统内核”——它不直接提供服务,而是为构建锁、信号量、门闩等“进程”提供统一的调度、内存和中断机制。

AQS 是 Java 并发编程的基石,其通过状态变量(state)FIFO 等待队列模板方法模式,为同步工具的实现提供了统一框架。理解 AQS 的核心原理,不仅能帮助我们更好地使用 JDK 中的并发工具,还能在需要时自定义高效的同步器。

AQS 的设计体现了 “高内聚、低耦合” 的思想:将复杂的队列管理、线程阻塞 / 唤醒等通用逻辑封装在抽象类中,子类只需关注具体的同步规则。这种设计极大地降低了并发组件的开发难度,也保证了这些组件在性能和可靠性上的一致性。


✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅
往期资料:
链接: https://pan.baidu.com/s/13wJhUxSVY2pbWOgjuIzWtg 提取码: dhq8
点击获取往期资料合集
提取码: dhq8
✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅✅

http://www.dtcms.com/a/346904.html

相关文章:

  • csrf漏洞学习笔记
  • C++ 20: Concepts 与Requires
  • 告别SaaS数据绑架,拥抱数据主权:XK+独立部署版跨境商城定制,为海外物流企业深度赋能
  • CentOS创建管理员用户feixue并设置密码全教程
  • 【c++进阶系列】:万字详解多态
  • 快速掌握Java非线性数据结构:树(二叉树、平衡二叉树、多路平衡树)、堆、图【算法必备】
  • STM32学习笔记19-WDG
  • linux shell测试函数
  • 百度深度学习面试:batch_size的选择问题
  • Linux总线设备驱动模型深度理解
  • 玩转Vue3高级特性:Teleport、Suspense与自定义渲染
  • 内联函数是什么以及的优点和缺点
  • ICP语序文字点选验证逆向分析(含Py纯算源码)
  • 基于SpringBoot+vue校园点餐系统
  • 【升级版】从零到一训练一个 0.6B 的 MoE 大语言模型
  • RabbitMQ面试精讲 Day 28:Docker与Kubernetes部署实践
  • JAVA核心基础篇-枚举
  • 【Linux网络编程】分布式Json-RPC框架 - 项目设计
  • Java试题-选择题(16)
  • 2025年渗透测试面试题总结-29(题目+回答)
  • 基于ResNet50的血细胞图像分类模型训练全记录
  • 2025-08-23 李沐深度学习19——长短期记忆网络LSTM
  • LeetCode 448.找到所有数组中消失的数字
  • 力扣 第 463 场周赛
  • 两款快速启动软件下载及安装!(GeekDesk和Lucy)!可图标归类!桌面更简洁
  • eBay运营全链路解析:从售后风控到生命周期营销的效率革命
  • 软件测试从入门到精通:通用知识点+APP专项实战
  • 基于STM32设计的养殖场环境监测系统(华为云IOT)_267
  • 8月23日星期六今日早报简报微语报早读
  • 施工场景重型车辆检测识别数据集(挖掘机、自卸卡车、轮式装载机):近3k图像,yolo标注