聊聊你对AQS的了解

AQS是Java并发包的基础组件,用于构建锁和其他同步组件。它包含state变量、加锁线程和等待队列等核心元素,通过FIFO队列管理资源获取线程的排队。AQS支持独占式和共享式获取同步状态,适用于多种同步组件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

AQS是一个并发包的基础组件,用来实现各种锁,各种同步组件的。

包含了state变量、加锁线程、等待队列等并发中的核心组件。

AQS全称是队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组件的基础框架。

它使用了一个int成员变量表示同步状态,默认情况下是0
/**

  • The synchronization state.
    */
    private volatile int state;

通过内置的FIFO队列来完成资源获取线程的排队工作。

同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程中免不了要对同步状态进行更改。

这时就需要使用同步器提供的3个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))来进行操作,因为它们能够保证状态的改变是安全的。

/**获取当前同步状态
protected final int getState() {
        return state;
    }
/**
设置当前同步状态
 */
protected final void setState(int newState) {
    state = newState;
}

```java
/**
 使用CAS设置当前状态,该方法能够保证状态设置的原子性。
 */
protected final boolean compareAndSetState(int expect, int update) {
    // See below for intrinsics setup to support this
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

同步器既可以支持独占式地获取同步状态,也可以支持共享式地获取同步状态,这样就可以方便实现不同类型的同步组件(ReentrantLock、ReentrantReadWriteLock和CountDownLatch)。

独占锁就是在同一时刻只能有一个线程获取到锁,而其他获取锁的线程只能处于同步队列中等待,只有获取锁的线程释放了锁,后继的线程才能够获取锁。

示例代码
class Mutex implements Lock {
// 静态内部类,自定义同步器
private static class Sync extends AbstractQueuedSynchronizer {
// 是否处于占用状态
protected boolean isHeldExclusively() {
return getState() == 1;
}
// 当状态为0的时候获取锁
public boolean tryAcquire(int acquires) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
// 释放锁,将状态设置为0
protected boolean tryRelease(int releases) {
if (getState() == 0) throw new
IllegalMonitorStateException();
setExclusiveOwnerThread(null);
setState(0);
return true;
}
// 返回一个Condition,每个condition都包含了一个condition队列
Condition newCondition() { return new ConditionObject(); }
}
// 仅需要将操作代理到Sync上即可
private final Sync sync = new Sync();
public void lock() { sync.acquire(1); }
public boolean tryLock() { return sync.tryAcquire(1); }
public void unlock() { sync.release(1); }
public Condition newCondition() { return sync.newCondition(); }
public boolean isLocked() { return sync.isHeldExclusively(); }
public boolean hasQueuedThreads() { return sync.hasQueuedThreads(); }
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}

在tryAcquire(int acquires)方法中,如果经过CAS设置成功(同步状态设置为1),则代表获
取了同步状态,而在tryRelease(int releases)方法中只是将同步状态重置为0。

可重入锁ReentrantLock和AQS的关系

顾名思义,就是支持重进入的锁,它表示该锁能够支持一个线程对资源的重复加锁。

ReentrantLock内部也是包含了一个AQS对象。

abstract static class Sync extends AbstractQueuedSynchronizer {
...
}

从这里理解等待队列,当线程1已经加完锁后,另外的线程再想加锁就将一个队列中等待锁的释放。

除此之外,该锁的还支持获取锁时的公平和非公平性选择。

<think>嗯,用户让我谈谈AQS理解AQS是AbstractQueuedSynchronizer的缩写,对吧?这是Java并发编程中非常重要的一个类。我记得它是在java.util.concurrent.locks包下,由Doug Lea设计的。它的主要作用是为实现同步器提供框架,比如ReentrantLock、Semaphore这些常用的同步工具都是基于AQS构建的。 首先,我需要回顾一下AQS的核心思想。AQS使用了一个int类型的state变量来表示同步状态,通过内置的FIFO队列来管理线程的排队工作。这样,开发者不需要自己处理复杂的线程阻塞和唤醒机制,只需要关注state的获取和释放逻辑。 用户可能想知道AQS的工作原理。比如,state变量是如何被操作的?我记得它是通过CAS(Compare and Swap)操作来保证线程安全的。比如,当线程尝试获取锁时,会检查state的值,如果是0,表示锁没有被占用,然后通过CAS将state设为1,成功的话就获取锁;否则,线程会被放入队列中等待。 接下来,AQS中的队列是CLH队列的变种,这是一个双向队列,每个节点代表一个等待的线程。节点中包含了线程的引用以及等待状态等信息。当线程无法获取锁时,会被构造成节点加入队列尾部,并在适当的时候被唤醒。 然后,用户可能关心的是AQS如何实现不同的同步器。比如,独占模式和共享模式的区别。独占模式下,只有一个线程能获取资源,如ReentrantLock;而共享模式下,多个线程可以同时获取,如Semaphore。AQS通过不同的模板方法来实现这两种模式,比如tryAcquire和tryAcquireShared。 我还需要提到AQS的模板方法模式。开发者需要重写特定的方法,比如tryAcquire、tryRelease、tryAcquireShared、tryReleaseShared等,来定义自己的同步逻辑。而AQS会处理线程的排队、阻塞和唤醒,这样开发者不需要关心底层的队列管理。 可能用户会问,为什么AQS这么重要?因为它抽象出了同步器的核心部分,让开发者可以更专注于业务逻辑,而不必重复实现复杂的线程调度。这也是Java并发包中许多工具高效可靠的原因。 另外,可重入性是如何实现的?比如ReentrantLock,当同一个线程多次获取锁时,state会递增,每次释放递减,直到为0才完全释放。这需要在tryAcquire和tryRelease方法中处理当前线程的判断和state的增减。 还有,AQS如何处理中断和超时?比如tryAcquireNanos方法允许线程在指定时间内获取锁,超时则返回false。而acquireInterruptibly方法支持响应中断,这些都需要在节点状态和唤醒机制中处理。 可能我还需要举几个例子,比如CountDownLatch,它的内部类Sync继承自AQS,初始化时state设为计数器的值,每次countDown()调用releaseShared,而await()调用acquireShared,直到state为0。 不过,用户可能对底层实现细节感兴趣,比如节点的入队和出队过程,如何确保线程安全,以及自旋和阻塞的平衡。比如,在入队时,可能使用CAS来保证原子性,而为了避免过多的CPU自旋,可能会在几次尝试后进入阻塞状态,依赖前驱节点的信号来唤醒。 还有,取消节点的情况如何处理?比如在获取锁超时或中断时,节点需要从队列中移除,并重新调整队列的状态,确保后续节点能正确被唤醒。 可能用户还会问到公平与非公平锁的区别。在AQS中,公平锁严格按照队列顺序获取锁,而非公平锁允许插队,新来的线程可能先尝试获取锁,而不是直接排队,这样提高了吞吐量但可能导致饥饿。 总结一下,AQS通过模板方法将同步状态的管理和线程排队机制抽象出来,让开发者可以灵活实现各种同步器,同时保证了高效和可靠性。理解AQS对于掌握Java并发编程至关重要,它是许多高级同步工具的基础。</think>AQS(AbstractQueuedSynchronizer)是 Java 并发编程的核心基础框架,用于构建锁和其他同步工具(如 `ReentrantLock`、`Semaphore`、`CountDownLatch` 等)。其核心思想是通过 **共享资源的状态管理** 和 **线程排队机制** 实现高效的线程同步。以下是关键点的总结: --- ### **1. 核心思想** - **同步状态(`state`)**: 通过一个 `volatile int` 变量表示资源状态(如锁的持有次数、信号量剩余量等),通过 CAS(Compare and Swap)操作保证原子性修改。 - **队列管理**: 使用一个 **CLH 变体的双向队列**(FIFO)管理竞争资源的线程。队列中的每个节点(`Node`)封装了等待线程的信息及其等待状态(如是否被取消)。 --- ### **2. 核心机制** #### **(1) 资源访问模式** - **独占模式(Exclusive)**: 资源仅允许一个线程持有(如 `ReentrantLock`)。通过 `tryAcquire` 和 `tryRelease` 定义获取/释放逻辑。 - **共享模式(Shared)**: 资源允许多个线程共享(如 `Semaphore`、`CountDownLatch`)。通过 `tryAcquireShared` 和 `tryReleaseShared` 实现。 #### **(2) 线程阻塞与唤醒** - 当线程获取资源失败时,会被封装为 `Node` 加入队列尾部,并通过 `LockSupport.park()` 阻塞。 - 当资源释放时,AQS 按队列顺序唤醒后续节点中的线程(公平性由实现决定)。 --- ### **3. 关键实现细节** #### **(1) 模板方法模式** 开发者需重写以下方法定义资源管理逻辑: - **独占模式**:`tryAcquire(int arg)`、`tryRelease(int arg)` - **共享模式**:`tryAcquireShared(int arg)`、`tryReleaseShared(int arg)` - **是否被当前线程独占**:`isHeldExclusively()` #### **(2) 可重入性** - 如 `ReentrantLock` 通过记录当前持有锁的线程和 `state` 重入次数实现。每次重入时 `state` 递增,释放时递减至 0 才完全释放资源。 #### **(3) 公平性与非公平性** - **公平锁**:严格按队列顺序分配资源,避免饥饿。 - **非公平锁**:允许新线程直接尝试插队获取资源(提高吞吐量)。 --- ### **4. 典型应用** - **ReentrantLock**: 基于 AQS 实现可重入的独占锁,支持公平/非公平策略。 - **Semaphore**: 通过共享模式控制并发线程数,`state` 表示可用许可证数量。 - **CountDownLatch**: `state` 初始化为计数器值,线程调用 `countDown()` 递减,`await()` 阻塞直到 `state` 归零。 --- ### **5. 优势与意义** - **解耦同步逻辑**:AQS 封装了线程排队、阻塞/唤醒等底层操作,开发者只需关注资源状态的管理。 - **高性能**:通过自旋、CAS 和队列优化减少上下文切换。 - **扩展性**:基于模板方法模式,可灵活实现各种同步工具。 --- ### **6. 示例:实现一个简易锁** ```java public class MyLock extends AbstractQueuedSynchronizer { @Override protected boolean tryAcquire(int arg) { if (compareAndSetState(0, 1)) { // CAS 尝试获取锁 setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } @Override protected boolean tryRelease(int arg) { if (getExclusiveOwnerThread() != Thread.currentThread()) { throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0); // 释放锁 return true; } public void lock() { acquire(1); } public void unlock() { release(1); } } ``` --- ### **总结** AQS 是 Java 并发包的基石,通过将复杂的线程同步问题抽象为 **状态管理** 和 **队列调度**,提供了高度灵活且高效的同步框架。理解其原理对掌握 `ReentrantLock`、`ThreadPoolExecutor` 等工具至关重要,也是优化高并发程序的关键。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值