Chiriri's blog Chiriri's blog
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)

Iekr

苦逼后端开发
首页
  • Java

    • JavaSE
    • JavaEE
    • 设计模式
  • Python

    • Python
    • Python模块
    • 机器学习
  • Golang

    • Golang
    • gRPC
  • 服务器

    • Linux
    • MySQL
    • NoSQL
    • Kubernetes
  • 项目

    • 传智健康
    • 畅购商城
  • Hadoop生态

    • Hadoop
    • Zookeeper
    • Hive
    • Flume
    • Kafka
    • Azkaban
    • Hbase
    • Scala
    • Spark
    • Flink
  • 大数据项目

    • 离线数仓
  • 青训营

    • 第四届青训营
  • HTML

    • HTML
    • JavaScript
  • Vue

    • Vue2
    • TypeScript
    • Vue3
    • Uni-APP
  • 数据结构与算法
  • C语言
  • 考研数据结构
  • 计算机组成原理
  • 计算机操作系统
  • Java基础

    • Java基础
    • Java集合
    • JUC
    • JVM
  • 框架

    • Spring
    • Dubbo
    • Spring Cloud
  • 数据库

    • MySQL
    • Redis
    • Elasticesearch
  • 消息队列

    • RabbitMQ
    • RocketMQ
  • 408

    • 计算机网络
    • 操作系统
    • 算法
  • 分类
  • 标签
  • 归档
  • 导航站
GitHub (opens new window)
  • JavaSE

  • JavaEE

  • Linux

  • MySQL

  • NoSQL

  • Python

  • Python模块

  • 机器学习

  • 设计模式

  • 传智健康

  • 畅购商城

  • 博客项目

  • JVM

  • JUC

    • 多线程
    • CompletableFuture
    • 锁
    • LockSupport与线程中断
    • Java内存模型之JMM
    • Volatile与Java内存模型
    • CAS
    • 原子操作类
    • ThreadLocal
    • Java对象内存布局和对象头
    • Synchronized与锁升级
    • AbstractQueuedSynchronizer之AQS
      • AQS为什么是JUC内容中最重要的基石
        • 进一步理解锁和同步器的关系
      • 能干什么
      • AQS初步
        • AQS初识
        • AQS内部体系架构
        • AQS自身
        • AQS的int变量
        • AQS的CLH队列
        • 总结
        • 内部类Node(Node类在AQS类内部)
        • Node的int变量
        • AQS同步队列的基本结构
      • 从ReentrantLock开始解读AQS
        • ReentrantLock的原理
        • 从最简单的lock方法开始看看公平和非公平
        • 非公平锁走起,方法lock()
        • unlock
    • ReentrantLock、ReentrantReadWriteLock、StampedLock讲解
    • JUC总结
    • 并发集合
  • Golang

  • Kubernetes

  • 硅谷课堂

  • C

  • 源码

  • 神领物流

  • RocketMQ

  • 短链平台

  • 后端
  • JUC
Iekr
2023-12-04
目录

AbstractQueuedSynchronizer之AQS

# AbstractQueuedSynchronizer 之 AQS

AbstractQueuedSynchronizer 字面意思抽象的队列同步器

image-20231204203727250

  • AbstractOwnableSynchronizer
  • AbstractQueuedLongSynchronizer
  • AbstractQueuedSynchronizer 简称为 AQS

AbstractQueuedSynchronizer 是用来构建锁或者其它同步器组件的重量级基础框架及整个 JUC 体系的基石,通过内置的 FIFO 队列来完成资源获取线程的排队工作,并通过一个 int 类变量表示持有锁的状态。

image-20231204203809661

CLH:Craig、Landin and Hagersten 队列,是一个单向链表,AQS 中的队列是 CLH 变体的虚拟双向队列 FIFO

# AQS 为什么是 JUC 内容中最重要的基石

和 AQS 有关的

image-20231204203837285

ReentrantLock

image-20231204203846199

CountDownLatch

image-20231204203855428

ReentrantReadWriteLock

image-20231204203905828

Semaphore

image-20231204203917972

# 进一步理解锁和同步器的关系

  • 锁,面向锁的使用者:定义了程序员和锁交互的使用层 API,隐藏了实现细节,你调用即可。
  • 同步器,面向锁的实现者:比如 Java 并发大神 DougLee,提出统一规范并简化了锁的实现,屏蔽了同步状态管理、阻塞线程排队和通知、唤醒机制等。

# 能干什么

加锁会导致阻塞,有阻塞就需要排队,实现排队必然需要队列

抢到资源的线程直接使用处理业务,抢不到资源的必然涉及一种排队等候机制。抢占资源失败的线程继续去等待 (类似银行业务办理窗口都满了,暂时没有受理窗口的顾客只能去候客区排队等候),但等候线程仍然保留获取锁的可能且获取锁流程仍在继续 (候客区的顾客也在等着叫号,轮到了再去受理窗口办理业务)。

既然说到了排队等候机制,那么就一定会有某种队列形成,这样的队列是什么数据结构呢?

如果共享资源被占用,就需要一定的阻塞等待唤醒机制来保证锁分配。这个机制主要用的是 CLH 队列的变体实现的,将暂时获取不到锁的线程加入到队列中,这个队列就是 AQS 的抽象表现。它将请求共享资源的线程封装成队列的结点(Node),通过 CAS、自旋以及 LockSupport.park () 的方式,维护 state 变量的状态,使并发达到同步的效果。

image-20231204204214819

# AQS 初步

# AQS 初识

image-20231204204246486

有阻塞就需要排队,实现排队必然需要队列,AQS 使用一个 volatile 的 int 类型的成员变量来表示同步状态,通过内置的 FIFO 队列来完成资源获取的排队工作将每条要去抢占资源的线程封装成一个 Node 节点来实现锁的分配,通过 CAS 完成对 State 值的修改。

# AQS 内部体系架构

# AQS 自身
# AQS 的 int 变量

AQS 的同步状态 State 成员变量

image-20231204204423351

银行办理业务的受理窗口状态

  • 零就是没人,自由状态可以办理
  • 大于等于 1,有人占用窗口,等着去
# AQS 的 CLH 队列

CLH 队列 (三个大牛的名字组成),为一个双向队列

image-20231204204501628

银行候客区的等待顾客

# 总结
  • 有阻塞就需要排队,实现排队必然需要队列
  • state 变量 + CLH 双端队列
# 内部类 Node (Node 类在 AQS 类内部)
# Node 的 int 变量

Node 的等待状态 waitState 成员变量

image-20231204204644235

等候区其它顾客 (其它线程) 的等待状态,队列中每个排队的个体就是一个 Node

Node 内部结构

image-20231204204704109

属性说明

image-20231204204714747

# AQS 同步队列的基本结构

image-20231204204730777

CLH:Craig、Landin and Hagersten 队列,是个单向链表,AQS 中的队列是 CLH 变体的虚拟双向队列(FIFO)

# 从 ReentrantLock 开始解读 AQS

Lock 接口的实现类,基本都是通过【聚合】了一个【队列同步器】的子类完成线程访问控制的

# ReentrantLock 的原理

image-20231204204806570

# 从最简单的 lock 方法开始看看公平和非公平

image-20231204204825221

image-20231204204832523

image-20231204204839455

image-20231204204850767

可以明显看出公平锁与非公平锁的 lock () 方法唯一的区别就在于公平锁在获取同步状态时多了一个限制条件: hasQueuedPredecessors() hasQueuedPredecessors 是公平锁加锁时判断等待队列中是否存在有效节点的方法

image-20231204204915843

# 非公平锁走起,方法 lock ()

本次讲解我们走非公平锁作为案例突破口

lock()

image-20231204205005332

acquire()

image-20231204205018279

image-20231204205023177

tryAcquire(arg) ,本次走非公平锁

image-20231204205036209

下一步:

image-20231204205043642

nonfairTryAcquire(acquires)

image-20231204205113951

  • return false; 继续推进条件,走下一个方法
  • return true; 结束

addWaiter(Node.EXCLUSIVE)

image-20231204205249351

enq(node);

image-20231204205257740

双向链表中,第一个节点为虚节点 (也叫哨兵节点),其实并不存储任何信息,只是占位。真正的第一个有数据的节点,是从第二个节点开始的。

假如 3 号 ThreadC 线程进来

  • prev
  • compareAndSetTail
  • next

acquireQueued(addWaiter(Node.EXCLUSIVE), arg)

image-20231204205428690

假如再抢抢失败就会进入

shouldParkAfterFailedAcquire 和 parkAndCheckInterrupt 方法中

image-20231204205535265

shouldParkAfterFailedAcquire

如果前驱节点的 waitStatus 是 SIGNAL 状态,即 shouldParkAfterFailedAcquire 方法会返回 true 程序会继续向下执行 parkAndCheckInterrupt 方法,用于将当前线程挂起

parkAndCheckInterrupt

image-20231204205606232

# unlock

  • sync.release(1);
  • tryRelease(arg)
  • unparkSuccessor
编辑 (opens new window)
上次更新: 2023/12/13, 06:06:02
Synchronized与锁升级
ReentrantLock、ReentrantReadWriteLock、StampedLock讲解

← Synchronized与锁升级 ReentrantLock、ReentrantReadWriteLock、StampedLock讲解→

最近更新
01
k8s
06-06
02
进程与线程
03-04
03
计算机操作系统概述
02-26
更多文章>
Theme by Vdoing | Copyright © 2022-2025 Iekr | Blog
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式