问小白 wenxiaobai
资讯
历史
科技
环境与自然
成长
游戏
财经
文学与艺术
美食
健康
家居
文化
情感
汽车
三农
军事
旅行
运动
教育
生活
星座命理

JUC中的AQS原理详解:从背景到源码

创作时间:
作者:
@小白创作中心

JUC中的AQS原理详解:从背景到源码

引用
CSDN
1.
https://blog.csdn.net/llg___/article/details/135003742

AQS(AbstractQueuedSynchronizer)是Java并发包(JUC)中的核心组件,用于实现锁和其他同步器组件。本文将从背景、概念、作用等多个维度深入解析AQS的工作原理,帮助读者理解其在并发编程中的重要地位和应用场景。

0、背景

在多线程编程中,我们经常需要对共享资源进行加锁和解锁操作。例如,以下是一个常见的加锁解锁代码:

Lock lock = new ReentrantLock();
lock.lock();
try{
    //do Something
} finally{
    lock.unlock();
}

当多个线程同时尝试获取锁时,抢不到锁的线程会被分配到哪里?它们是如何排队等待的?队列底层又是如何维护的?这些问题的答案都与AQS密切相关。

1、AQS介绍

AQS,即AbstractQueuedSynchronizer,是JUC中的基石组件,类似于JVM之于Java。AQS主要用于实现锁或其他同步器组件的公共基础部分,是一个重量级的基础框架,主要用于解决锁分配给"谁"的问题。

AQS相关的类包括:

  • AbstractOwnableSynchronizer(下面两兄弟的父类)
  • AbstractQueuedLongSynchronizer:自JDK 1.6起引入
  • AbstractQueuedSynchronizer:简称AQS,自JDK 1.5起引入

这些类都是抽象类,完成了一部分逻辑,剩余部分定义了方法规范,供子类继承和重写。

2、AQS核心概念

AQS的核心概念可以概括为"先进先出的一个队列 + state状态值"。可以将其类比为银行办理业务的场景:

  • 当有人开始办理业务时,指示灯变红(state=1,表示有人占用资源),其余人去候客区的椅子上等待。
  • 这个候客区就是一个抽象的FIFO队列,通过一个int类型的变量表示持有锁的状态。
  • 这个队列被称为CLH队列,是Craig、Landin和Hagersten三人提出的单向链表的变体,但在AQS中实现为虚拟的双向队列FIFO。

3、AQS是JUC的基石

AQS之所以成为JUC的基石,是因为JUC中的许多组件都是基于AQS实现的。例如,ReentrantLock、Semaphore、CountDownLatch等都依赖于AQS来实现同步控制。

4、锁和同步器的关系

在并发编程中,锁和同步器扮演着不同的角色:

  • :面向普通开发者,提供了lock、unlock等API,隐藏了实现细节。
  • 同步器:面向锁的实现者,提供了统一的规范和简化了锁的实现,屏蔽了同步状态管理、同步队列的管理和维护、阻塞线程排队和通知唤醒机制等公共的底层细节。

5、AQS的作用

AQS的主要作用是处理线程的阻塞和唤醒机制。当共享资源被占用时,需要一定的阻塞、唤醒机制来保证锁的分配。这个机制主要通过CLH队列的变体实现,将暂时获取不到锁的线程加入到队列中。

具体来说,AQS使用以下机制实现同步效果:

  • 一个volatile的int类型的成员变量state来表示同步状态
  • 内置的FIFO队列来完成资源获取的排队工作
  • 将每个请求共享资源的线程封装成队列的结点对象Node
  • 通过CAS自旋和LockSupport.park()的方式维护state变量的状态

6、公平锁与非公平锁

基于AQS框架开发的锁,其公平性主要体现在新线程如何参与锁的竞争:

  • 非公平锁:新来的线程与队列中的线程共同抢锁,通过CAS竞争state值。
  • 公平锁:新来的线程排到FIFO队列的末尾,只让队列中的head线程获得锁。

7、state和CLH队列

AQS类中包含一个int类型的成员变量state,用于表示同步状态:

private volatile int state;

这个state变量类似于银行办理业务的受理窗口状态:0表示没人占用,大于等于1表示有人占用。而CLH队列就是候客区,队列中装的是一个个Node内部类的对象。

总结来说,AQS的实质就是一个CLH的双端队列加上一个state变量,队列中排队的每个个体就是一个Node。

8、AQS的内部类Node

AQS的内部类Node用于表示队列中的每个节点,其成员变量含义如下:

static final class Node {
    /** 共享模式 */
    static final Node SHARED = new Node();
    /** 独占模式 */
    static final Node EXCLUSIVE = null;
    /** 线程被取消了 */
    static final int CANCELLED =  1;
    /** 后继线程需要被唤醒 */
    static final int SIGNAL    = -1;
    /** 等待condition唤醒 */
    static final int CONDITION = -2;
    /** 共享式同步状态获取将会无条件地传播下去 */
    static final int PROPAGATE = -3;
    /** 等待状态 */
    volatile int waitStatus;
    /** 前置节点 */
    volatile Node prev;
    /** 后置节点 */
    volatile Node next;
    /** 坐在Node里的线程 */
    volatile Thread thread;
    // 构造方法、实例方法、静态代码块略
}

每个Node节点包含了线程的等待状态、前后节点引用以及线程本身的信息,通过这些信息实现线程的排队和唤醒机制。

© 2023 北京元石科技有限公司 ◎ 京公网安备 11010802042949号