深入理解AQS(抽象队列同步器)

本文详细解析了AbstractQueuedSynchronizer(AQS)在Java并发编程中的核心作用,包括如何通过继承AQS创建自定义锁,关键方法的重写和模板方法的使用,以及同步队列的实现和同步状态的获取与释放。实例演示了互斥锁的实现,对比了Condition接口与Object监视器的区别,并深入剖析了Condition的内部机制。

1. 前言

        抽象队列同步器AbstractQueuedSynchronizer,是用来构建java.util.concurrent包中的锁和其他并发相关的组件的基础类。它通过使用一个int类型的state来表示同步状态,将没有获取锁的线程构造成一个节点,放到一个先进先出队列里,这个队列是使用链表实现的。当一个线程释放锁后,会将这个队列里的节点对应的线程唤醒。

        AQS是抽象类,所以使用时需要继承AQS,实现所有protected方法。子类应该定义为非public的内部帮助类(helper class),用来实现外部类的同步属性。

2. AQS中的主要方法

        AQS是基于模板方法模式设计的,所以使用时需要继承AQS并重写指定的方法。然后将AQS组合在自定义同步组件的实现中,并调用AQS提供的模板方法,而这些模板方法将会调用重写的方法。

2.1 子类可重写的方法

重写AQS指定的方法时,需要使用AQS提供的下面3个方法来获取,修改同步状态。

  • getState():获取当前同步状态
  • setState():设置当前同步状态
  • compareAndSetStatus(int expect, int update):使用CAS设置同步状态,该方法可以保证原子性

同步器可重写的方法如下:

方法

描述

protected protected boolean tryAcquire(int arg)

尝试独占式获取同步状态。这个方法应该先检查对象的状态是否允许独占式获取,如果是将获取同步状态

protected boolean tryRelease(int arg)

尝试独占式设置状态以释放锁

protected int tryAcquireShared(int arg)

尝试共享式获取同步状态。这个方法应该先检查对象的状态是否允许共享式获取,如果是将获取同步状态。

返回负数表示失败,其他表示成功

返回值详细说明:

a negative value on failure; zero if acquisition in shared mode succeeded but no subsequent shared-mode acquire can succeed; and a positive value if acquisition in shared mode succeeded and subsequent shared-mode acquires might also succeed, in which case a subsequent waiting thread must check availability. (Support for three different return values enables this method to be used in contexts where acquires only sometimes act exclusively.) Upon success, this object has been acquired.

protected boolean tryReleaseShared(int arg)

尝试共享式设置状态以释放锁

protected boolean isHeldExclusively()

当前线程是否在独占模式下占用同步状态(synchronization)

2.2 模板方法

实现自定义同步组件时,将会调用AQS提供的模板方法,部分方法如下:

方法

描述

public final void acquire(int arg)

独占式获取同步状态,忽略中断。通过至少一次调用tryAcquire方法实现

如果获取成功,则会返回,否则,线程将会进入同步队列,可能会重复地阻塞,非阻塞(unblocking),调用tryAcquire,直到成功

public final void acquireInterruptibly(int arg)

在acquire(int arg)基础上增加了中断响应

public final boolean tryAcquireNanos(int arg, long nanosTimeout)

在acquireInterruptibly(int arg)基础上增加了超时限制

public final void acquireShared(int arg)

共享式获取同步状态,忽略中断。通过先至少调用一次tryAcquireShared实现。与独占式获取的主要区别是在同一时刻可以有多个线程获取到同步状态

public final void acquireSharedInterruptibly(int arg)

在acquireShared(int arg)基础上增加了中断响应

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout)

在acquireSharedInterruptibly(int arg)基础上增加了超时限制

public final boolean release(int arg)

独占式释放同步状态。该方法会在释放同步状态后,将同步队列中第一个节点包含的线程唤醒

public final boolean releaseShared(int arg)

共享式释放同步状态

public final Collection getQueuedThreads()

获取等待在同步队列上的线程集合

        上面一共有三类方法:独占式获取与释放同步状态、共享式获取与释放同步状态和查询同步队列中的等待线程情况。

3. 代码示例

        下面这个类是官方给的示例。互斥(独占)锁是一个自定义同步组件,它在同一时刻只允许一个线程占有锁。

/**
 * Here is a non-reentrant mutual excl
### 抽象队列同步器 AQS 的原理与实现 #### 1. AQS 的定义与作用 AQS(AbstractQueuedSynchronizer)是 Java 并发包 `java.util.concurrent` 中的一个核心组件,它提供了一种框架化的机制来构建锁和其他同步工具。AQS 使用了一个基于 FIFO 的双向链表队列来管理线程之间的竞争关系,并通过状态变量控制线程的同步行为[^1]。 #### 2. 核心组成部分 ##### (1)状态变量(state) AQS 维护一个名为 `state` 的整型变量,用来表示同步状态。不同的子类可以通过原子操作修改这个变量以反映资源的占有情况。例如,在 ReentrantLock 中,`state` 表示当前锁被持有的次数;而在 Semaphore 中,`state` 则代表剩余信号量的数量[^3]。 ##### (2)CLH 队列 为了高效地处理大量线程的竞争,AQS 内部采用 CLH(Craig, Landin, and Hagersten locks)变体形式的队列结构。当一个线程试图获取锁失败时,会被封装成节点加入到该队列中等待下一次机会。每个节点包含了指向前后相邻节点的指针以及一些标志位用于记录自身的状况[^4]。 #### 3. 主要方法分类 根据职责划分,AQS 定义了一系列模板方法供开发者覆盖实现: - **独占模式 vs 共享模式** - 独占模式意味着同一时刻只有一个线程能成功获得许可执行关键路径上的代码片段。 - 共享模式允许多个线程同时持有相同的权限级别而不违反约束条件。 - **tryAcquire/tryRelease 系列函数** 这些是非公平版本下的尝试性接口,默认情况下不考虑排队顺序直接抢夺资源使用权。如果调用者满足准入资格则返回 true ,否则 false 。对于释放动作而言则是更新内部计数器并将可能唤醒下一个候选者[^2]。 - **acquire/acquireShared 方法族** 正常途径进入临界区的方式分为两类:一是完全遵循既定规则逐级向上申请直至达成目的为止;二是允许一定程度上的投机取巧即所谓的“乐观锁定策略”。 - **release/releaseShared 函数组** 类似于上面提到的内容只不过方向反转过来而已——前者是从拥有权角度出发逐步削减直到彻底放弃掌控权;后者强调集体利益最大化原则之下共同退让部分权益给后来者享用[^2]。 #### 4. 锁定机制详解 以下是有关如何运用 AQS 构建自定义同步原语的一些简单例子: ```java public class MyMutex extends AbstractQueuedSynchronizer { protected boolean tryAcquire(int acquires) { if (compareAndSetState(0, 1)) { // CAS 更新 state 值 setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } protected boolean tryRelease(int releases) { if (getState() == 0) throw new IllegalMonitorStateException(); setState(0); // 清零恢复初始态 setExclusiveOwnerThread(null); // 解绑当前所有者信息 return true; // 返回值决定是否通知后续待命成员 } public void lock() { acquire(1); } public void unlock() { release(1); } } ``` 上述代码展示了怎样借助 AQS 来模拟互斥锁的行为特性。其中重点在于重写两个虚基类提供的纯虚函数分别对应加锁解锁两阶段的操作逻辑[^3]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值