AbstractQueuedSynchronizer之AQS

简介: AbstractQueuedSynchronizer之AQS

AbstractQueuedSynchronizer(AQS

概述

AbstractQueuedSynchronizer(简称AQS)是Java中锁和同步器的基础框架,大多数的锁和同步器都是通过继承AQS来实现的。AQS提供了一个灵活的框架,使得开发者可以根据自己的需求来实现各种锁和同步器。本文将介绍AQS的基本原理和实现细节。

基本原理

AQS维护了一个state(状态)属性和一个等待队列。state用来表示同步状态,当state为0时表示锁没有被占用,当state不为0时表示锁已经被占用,state的含义和具体使用方法是由子类定义的。等待队列则用来存储那些请求锁或者同步器的线程。

当一个线程请求锁或同步器时,如果此时state为0,则表示当前线程可以获得锁或同步器,此时会将state设置为1,表示当前锁或同步器已经被占用了。如果此时state不为0,则表示此时锁或同步器正在被其他线程占用,此时当前线程会被加入到等待队列中,等待其他线程释放锁或同步器。

当其他线程释放了锁或同步器时,会唤醒等待队列中的一个线程,使其重新尝试获取锁或同步器。如果此时该线程又成功获取了锁或同步器,则该线程会从等待队列中移除。

实现细节

等待队列

AQS的等待队列是一个双向链表,每个节点表示一个等待线程(或者称为Node),Node继承自AbstractQueuedSynchronizer。下面是Node的定义:

static final class Node {
    // 共享模式
    static final Node SHARED = new Node();
    // 独占模式
    static final Node EXCLUSIVE = null;
    // waitStatus的值表示当前节点需要等待的状态
    static final int CANCELLED =  1;
    static final int SIGNAL    = -1;
    static final int CONDITION = -2;
    static final int PROPAGATE = -3;
    volatile int waitStatus;
    volatile Node prev;
    volatile Node next;
    volatile Thread thread;
    Node nextWaiter;
    final boolean isShared() {
        return nextWaiter == SHARED;
    }
    final Node predecessor() throws NullPointerException {
        Node p = prev;
        if (p == null)
            throw new NullPointerException();
        else
            return p;
    }
    Node() {
    }
    Node(Thread thread, Node mode) {     // Used by addWaiter
        this.nextWaiter = mode;
        this.thread = thread;
    }
    Node(Thread thread, int waitStatus) { // Used by Condition
        this.waitStatus = waitStatus;
        this.thread = thread;
    }
}

Node节点有以下属性:

  • waitStatus:表示节点的状态,可以是以下值:
  • CANCELLED:表示节点已经取消。
  • SIGNAL:表示在释放同步器或者锁时需要唤醒该节点。
  • CONDITION:表示节点在等待Condition时被阻塞。
  • PROPAGATE:表示共享模式下下一个节点需要唤醒。
  • prev:表示当前节点的前一个节点。
  • next:表示当前节点的后一个节点。
  • thread:表示当前节点所对应的线程。
  • nextWaiter:表示下一个等待节点。

等待队列的头节点是一个哑节点,不表示任何一个等待线程。当等待队列不为空时,队首节点表示正在占用同步器或者锁的线程,队列中剩下的节点表示正在等待获取同步器或者锁的线程。

等待队列的操作主要有以下几个:

  • enq(node):将一个节点加入等待队列的尾部。
  • addWaiter(mode):创建一个新的节点并加入到等待队列的尾部。mode表示该节点的模式,可以是独占模式或共享模式。
  • setHead(node):设置等待队列的头节点。
  • unparkSuccessor(node):唤醒等待队列中最近的一个还未被取消的节点。在独占模式下,需要唤醒的节点是当前节点的后继节点;在共享模式下,需要唤醒的节点是等待队列中第一个未被取消的节点。

state属性

AbstractQueuedSynchronizer中,state属性是一个volatile类型的整数,用来表示同步状态。子类可以通过修改state的值来实现自己的同步机制。

state为0时表示同步器或锁没有被占用,当state不为0时表示同步器或锁已经被占用。在独占模式下,state的值为1表示锁已经被占用,在共享模式下,state的值表示正在使用锁的线程数。

state的主要操作有以下几个:

  • getState():获取当前状态的值。
  • setState(newState):设置当前状态的值。
  • compareAndSetState(expect, update):原子性地将当前状态和expect比较,如果相等则将其设置为update,并返回true,否则返回false。

独占模式

AbstractQueuedSynchronizer中的独占模式是最常用的同步机制之一,代表了Java中的锁。

我们来看一下ReentrantLock是如何实现独占模式的。

ReentrantLock

ReentrantLock是一个可重入的互斥锁,它支持公平锁和非公平锁。ReentrantLock继承自AbstractQueuedSynchronizer,在ReentrantLock的构造方法中会调用AbstractQueuedSynchronizer的构造方法来初始化state属性和等待队列。


相关文章
|
7月前
BigDecimal保留两位小数
本文介绍了BigDecimal保留两位小数的三种方法:`setScale`、`DecimalFormat`和`String.format`。其中,`setScale`可设置保留规则并返回BigDecimal类型值;`DecimalFormat`通过匹配规则返回字符串类型值;`String.format`为字符串自带方法,同样返回字符串类型值。此外,文章还对比了四种保留小数规则(如`00.00`、`#0.00`等),总结出`#0.00`是最适用的规则。附有详细代码示例与控制台打印结果,便于理解与实践。
1037 19
|
12月前
|
存储 缓存 Java
HashMap源码剖析-put流程
更好地掌握 `HashMap` 的内部实现原理,提高编写高效代码的能力。掌握这些原理不仅有助于优化性能,还可以帮助解决实际开发中的问题。
341 13
|
缓存 NoSQL Java
避免缓存失效的三大杀手:缓存击穿、穿透与雪崩的解决方案
避免缓存失效的三大杀手:缓存击穿、穿透与雪崩的解决方案
1651 0
|
缓存 算法 Java
这些年背过的面试题——JVM篇
本文是技术人面试系列JVM篇,面试中关于JVM都需要了解哪些基础?一文带你详细了解,欢迎收藏!
|
XML Java Maven
Springboot Starter 是如何工作的?
Springboot Starter 是 Springboot 项目的一部分,简化了依赖管理和自动配置,通过 Maven 或 Gradle 引入相关依赖并自动配置应用程序。其核心特性包括依赖管理、自动配置及条件注解。Starter 的设计思维体现了模块化、约定优于配置、依赖注入等原则,提高了开发效率,但也存在调试复杂、过度依赖等问题。
471 3
|
安全 程序员 编译器
C++程序中的基类与派生类转换
C++程序中的基类与派生类转换
217 1
|
存储 关系型数据库 MySQL
【高频】什么是索引的下推和覆盖
【高频】什么是索引的下推和覆盖
794 2
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
【Azure API 管理】在 Azure API 管理中使用 OAuth 2.0 授权和 Azure AD 保护 Web API 后端,在请求中携带Token访问后报401的错误
266 0
基于ADM自适应增量调制算法的matlab性能仿真
该文主要探讨基于MATLAB的ADM自适应增量调制算法仿真,对比ADM与DM算法。通过图表展示调制与解调效果,核心程序包括输入输出比较及SNR分析。ADM算法根据信号斜率动态调整量化步长,以适应信号变化。在MATLAB中实现ADM涉及定义输入信号、初始化参数、执行算法逻辑及性能评估。
|
缓存 NoSQL 算法
Redis 解决缓存雪崩 缓存穿透 缓存击穿(Redis使用必看)
Redis 解决缓存雪崩 缓存穿透 缓存击穿(Redis使用必看)
516 1

热门文章

最新文章