深入理解 Java 并发:AbstractQueuedSynchronizer 源码分析

本文详细解读了Java并发包中的AQS(AbstractQueuedSynchronizer)同步框架,包括其核心实现原理(基于等待队列和同步状态)、工作原理、关键方法以及在ReentrantLock、ReentrantReadWriteLock、CountDownLatch和Semaphore等同步组件中的应用。
摘要由CSDN通过智能技术生成

序言

在多线程编程中,同步机制是保障线程安全和协调线程之间操作顺序的重要手段。AQS 作为 Java 中同步机制的基础框架,为开发者提供了一个灵活且高效的同步工具。本文将通过对 AQS 源码的分析,解读 AQS 的核心实现原理,并深入探讨其在不同同步组件中的应用。

一、什么是 AQS

workspace (4).jpg

AQS 是 AbstractQueuedSynchronizer 的缩写,是 Java 并发包中提供的一个用于实现各种锁和同步器的基础框架。它提供了一种灵活而强大的机制,可以帮助开发者实现各种自定义的锁和同步组件。

AQS 的设计思想是基于等待队列(Wait Queue)和同步状态(Sync State)。它通过维护一个等待队列来管理等待获取同步资源的线程,并使用一个同步状态来表示当前锁或同步器的状态。AQS 提供了一系列方法来操作同步状态和等待队列,包括获取同步状态、释放同步状态、加入等待队列等操作,以及唤醒等待队列中的线程等操作。

AQS 主要包含两个核心方法:tryAcquire 和 tryRelease。这两个方法由具体的同步器实现,用于控制同步状态的获取和释放。通过这两个方法的协作,AQS 可以实现各种类型的锁和同步器,包括独占锁、共享锁、读写锁、信号量等。

AQS 提供了一种基于条件等待和通知的高级线程同步机制,能够满足各种复杂的并发编程需求。它被广泛应用于 Java 并发编程中的各种锁和同步组件的实现,如 ReentrantLock、ReentrantReadWriteLock、CountDownLatch、Semaphore 等。 AQS 提供了一个灵活、高效和可扩展的框架,使得开发者可以轻松地实现自定义的并发控制组件,并充分发挥 Java 并发包提供的丰富功能。

二、AQS 源码结构分析

public abstract class AbstractQueuedSynchronizer
extends AbstractOwnableSynchronizer
implements java.io.Serializable {

    // 构造器
    protected AbstractQueuedSynchronizer() { }

    // Node 内部类
    static final class Node {}

    // 构造链表等待队列的头节点
    private transient volatile Node head;

    // 构造链表等待队列的尾节点
    private transient volatile Node tail;

    // 同步状态
    private volatile int state;

    // tryAcquire 方法
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }

    // tryRelease 方法
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

    // ConditionObject 内部类
    public class ConditionObject implements Condition, java.io.Serializable {}

    // 省略了其他方法或属性

}

上面是 AbstractQueuedSynchronizer 抽象类的源码,省略了一部分内容,保留了较为核心的内容,我们可以看出其核心结构如下:

  1. 同步状态(state):AQS 内部维护了一个同步状态变量,通常是一个整数,用于表示同步器的状态。这个同步状态是 AQS 控制同步和并发访问的核心。
  2. 等待队列(Wait Queue):AQS 使用等待队列来管理等待获取同步资源的线程。等待队列通常是一个基于 FIFO(先进先出)的数据结构(使用链表实现),确保等待线程的公平竞争和顺序执行。
  3. Node 类:等待队列中的每个节点都是 Node 类的实例,它包含了线程的相关信息以及用于构建队列的链接信息。Node 类中通常包含了指向前驱节点和后继节点的引用,用于构建双向链表。
  4. 核心方法:acquire 和 release:用于获取和释放同步状态的方法,是 AQS 中最核心的方法之一。tryAcquire 和 tryRelease:尝试获取和释放同步状态的方法,是具体同步器需要实现的抽象方法。
  5. 条件对象(ConditionObject):条件对象是 AQS 提供的一种机制,用于管理条件队列。

三、AQS 工作原理

workspace (2).jpg

AQS 的核心思想是基于状态的抽象和等待队列的管理。具体来说,当一个线程尝试获取锁或者同步资源时,如果失败了,它会被包装成一个节点加入到等待队列中,然后自旋等待获取锁。当锁的释放动作发生时,AQS 会按照特定的规则选择一个节点来唤醒,从而实现线程的竞争和同步。

四、AQS 关键方法解析

4.1 acquire() 方法

public final void acquire(int arg) {
if (!tryAcquire(arg) &&
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
}

acquire 是 AQS 定义获取锁的方法。该方法看似简单其实做了许多事情。首先,尝试使用 tryAcquire 方法来快速尝试获取同步状态,如果失败则将当前线程加入到等待队列中,并自旋等待直到成功获取同步状态为止。如果线程在等待过程中被中断,则会自我中断。

acquire 方法被 final 关键字修饰了,是不可被重写的。acquire 方法其实只是定义了流程,具体逻辑是下放给 tryAcquire 方法了。

protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

从上面的源码可以看出 tryAcquire 方法没有具体的实现,需要交由其子类实现。

4.2 release() 方法

public final boolean release(int arg) {
// 尝试释放锁
if (tryRelease(arg)) {
    // 获取当前等待队列的头节点
    Node h = head;
    // 检查头节点是否存在且其等待状态不为 0
    if (h != null && h.waitStatus != 0)
        // 唤醒后继节点
        unparkSuccessor(h);
    // 释放锁成功,返回 true
    return true;
}
// 释放锁失败,返回 false
return false;
}

release 是 AQS 定义释放锁的方法。该方法与获取锁的 acquire 方法类似,只是定义了释放锁的流程,具体的释放逻辑是交由子类去实现 tryRelease 方法。

五、AQS 深入分析

等待队列与部分核心方法上文已经有过解释了。AQS 剩下的核心内容还有:

  1. 同步状态(state)
  2. Node 类
  3. 条件对象(ConditionObject)

image.png

同步状态是一个整数变量,通常被命名为 state。同步状态用于表示当前同步器的状态信息,可以被不同类型的同步器用于不同的目的。具体来说,同步状态可以代表某种资源的数量、某种资源的可用性或者某种锁的状态等。例如,在独占锁(Exclusive Lock)中,同步状态可以表示锁的占用情况;在共享锁(Shared Lock)中,同步状态可以表示锁的共享数量;在 Semaphore(信号量)中,同步状态可以表示可用的许可数量等。

Node 类是 AQS 中的一个内部类,主要用于构建链表形式的等待队列。每个节点都代表一个等待获取同步资源的线程。

在并发编程中,等待唤醒机制是一种重要的线程同步机制,用于实现线程之间的协作和通信。而 AQS 提供了条件队列用于实现等待唤醒机制,条件队列通常与条件对象一起使用,条件对象是 AQS 提供的一种机制,用于管理条件队列

六、AQS 在锁和同步组件中的应用

AQS 只是一个抽象类,我们想要使用它必须要实现相关的逻辑。而 Java 基于 AQS 已经给我们提供了许多的实现:

  1. ReentrantLock(重入锁):ReentrantLock 是 Java 并发包提供的一种独占锁实现,它使用了 AQS 来实现锁的底层逻辑。ReentrantLock 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来控制锁的获取和释放,从而实现可重入性和线程安全性。
  2. ReentrantReadWriteLock(重入读写锁):ReentrantReadWriteLock 是 Java 并发包提供的一种读写锁实现,它也使用了 AQS 来实现锁的底层逻辑。ReentrantReadWriteLock 内部维护了两个 AQS 实例,分别用于读锁和写锁的管理。通过 AQS 的 acquire 和 release 方法来控制读锁和写锁的获取和释放,从而实现读写锁的功能。
  3. CountDownLatch(倒计时门栓):CountDownLatch 是 Java 并发包提供的一种同步工具,它使用了 AQS 来实现同步等待的逻辑。CountDownLatch 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来实现等待和通知的功能。线程调用 await 方法等待计数器归零,而其他线程调用 countDown 方法来减少计数器的值,当计数器归零时,等待的线程被唤醒继续执行。
  4. Semaphore(信号量):Semaphore 是 Java 并发包提供的一种同步工具,它也使用了 AQS 来实现信号量的逻辑。Semaphore 内部持有一个 AQS 实例,通过 AQS 的 acquire 和 release 方法来控制信号量的获取和释放。线程调用 acquire 方法尝试获取信号量,而其他线程调用 release 方法来释放信号量,从而控制同时访问资源的数量。

推荐阅读

  1. 深入了解 Arthas:Java 应用程序诊断利器
  2. 基于 AI 的数据库助手-Chat2DB
  3. EasyExcel 处理 Excel
  4. 实体映射解决方案-MapStruct
  5. 动态切换数据源的最佳实践
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值