Java中并发神器之鬼斧神功AQS及工作原理

本文详细介绍了Java中的并发神器AQS(AbstractQueuedSynchronizer),包括其概念、核心思想、组件和设计思想。AQS是构建多线程同步机制的框架,用于实现如ReentrantLock、Semaphore等并发工具。AQS通过CLH队列锁管理线程的阻塞和唤醒,支持独占和共享两种模式。在自定义同步器时,主要实现tryAcquire和tryRelease等方法。

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

一、AQS的概念

AQS全称AbstractQueuedSynchronizer,即抽象的队列同步器,是一种构建多线程同步机制的框架。AQS 解决了在实现同步器时涉及的大量细节问题,如自定义标准同步状态、FIFO 同步队列等。基于 AQS 来构建同步器可以带来很多好处,它不仅能够极大地减少实现工作,而且也不必处理在多个位置上发生的竞争问题。
Java中基于AQS构建的并发同步器有:

  • ReentrantLock
  • Semaphore
  • CountDownLatch
  • ReentrantReadWriteLock
  • SynchronusQueue
  • CyclicBarrier等
    如图,通过AQS 抽象同步队列可以看到AQS构建的相关并发同步器:
    在这里插入图片描述

二、AQS核心知识铺垫

2.1 核心思想

A Q S 的核心思想为 \color{red}{AQS的核心思想为} AQS的核心思想为: 若被请求的共享资源空闲,则将当前请求共享资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。
C L H 队列 \color{red}{CLH队列} CLH队列(Craig,Landin,and Hagersten)是一个虚拟的双向队列,虚拟的双向队列即不存在队列实例,仅存在节点之间的关联关系。
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的一个结点(Node),来实现锁的分配。

如图所示:
在这里插入图片描述

2.2 核心组件

  • Sync queue: 同步队列,是一个双向列表,包括head节点和tail节点。head节点主要用作后续的调度。
  • Condition queue: 非必须,单向列表,当程序中存在condition的时候才会存在此列表。

2.3 核心设计思想

  • 同步状态:AQS使用一个int成员变量来表示同步状态;
  • 队列:使用Node实现FIFO队列,可以用于构建锁或者其他同步装置;
  • AQS资源共享方式
    • 独占Exclusive=>排它锁模式
    • 共享Share=>共享锁模式
      使用AQS实现的并发工具中,要么实现并使用了AQS的独占功能API,要么实现并使用了共享锁的功能API,而不会同时使用两套API,即便是最有名的子类ReentrantReadWriteLock也是通过两个内部类读锁和写锁分别实现了两套api来实现的。
      在构建自定义同步器时,只需要依赖AQS底层再实现共享资源state的获取与释放操作即可。自定义同步器实现时主要实现以下几种方法:
  • 自定义同步器的实现
    • isHeldExclusively():该线程是否正在独占资源。只有用到condition才需要去实现它。
    • tryAcquire(int):独占方式。尝试获取资源,成功则返回true,失败则返回false。
    • tryRelease(int):独占方式。尝试释放资源,成功则返回true,失败则返回false。
    • tryAcquireShared(int):共享方式。尝试获取资源。负数表示失败;0表示成功,但没有剩余可用资源;正数表示成功,且有剩余资源。
    • tryReleaseShared(int):共享方式。尝试释放资源,如果释放后允许唤醒后续等待结点返回true,否则返回false。

三、AQS的实现原理

整体 实现原理 \color{red}{实现原理} 实现原理为:
线程在竞争资源时,首先尝试获取锁,若失败则将当前线程及等待状态等信息包装为一个Node节点加入到FIFO队列中。 若当前节点为Head头结点的直接后继节点则会不断循环尝试获取锁,否则非Head的直接后继节点时,失败后就会阻塞自己直到被唤醒。当持有锁的线程释放锁的时候,会唤醒队列中后继节点中的线程。

3.1 独占模式的AQS原理

同步状态的获取: 独占模式即在某一时刻只允许一个线程获取同步状态,当这个线程还未释放同步状态时,其他线程是无法获取的,需要加入到同步队列中进行等待。
该种模式下,我们可以将state的初始值设为0表示空闲。当某一个线程获取到同步状态时,使用CAS操作让state加1,表示非空闲,那么其他线程就只能等待了。
同步状态的释放: 在释放同步状态时,则不需要CAS操作,因为独占模式下只有一个线程能获取到同步状态。
ReentrantLock、CyclicBarrier正是基于此设计的。
其中ReentrantLock的state初始化为0表示未锁定状态。如下图,A线程lock()时,会调用tryAcquire()独占该锁并将state+1。
在这里插入图片描述
独占模式下的AQS是不响应中断,而是再次去判断其前驱节点是否为head节点,决定是否争抢同步状态。如果其前驱节点不是head节点或者争抢同步状态失败,那么再次挂起。加入到同步队列中的线程,如果因为中断而被唤醒的话,不会立即返回并且抛出InterruptedException。

3.2 共享模式的AQS原理

同步状态的获取: 共享模式允许多个线程同时获取到同步状态,共享模式下的AQS也是不响应中断的。该模式下我们可以将state的初始值设为N(N > 0),表示空闲。每当一个线程获取到同步状态时,就利用CAS操作让state减1,直到减到0表示非空闲,其他线程就只能加入到同步队列,进行等待。
同步状态的释放: 释放同步状态时,需要使用CAS进行操作,因为共享模式下,可能会有多个线程能获取到同步状态。
CountDownLatch、Semaphore正是基于此设计的。
如下图,CountDownLatch的任务分为N个子线程去执行,同步状态state也初始化为N(注意此时N要与线程个数保持一致):
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值