AQS机制

article/2025/10/7 18:05:12

1、什么是AQS?
     AQS是抽象同步队列,基于CAS和LockSupport实现,通过资源状态state和AQS的同步队列实现线程抢占资源的管理。
2、获取资源
      线程进来先获取资源,如果失败会重试一次,再次失败会将当前线程存放至同步队列,并调用park()阻塞当前线程。
3、释放资源
      当线程释放资源的时候,会获取到同步队列中的头节点的next节点(因为头节点是当前线程),将next节点设置为头节点,并调用unpark()唤醒新头节点的线程去获取锁资源。
4、可重入锁
      state的为2、3时,表示该资源被重入了。其原理为:当第一次获取资源失败,重试时会判断获取资源的线程是否为当前线程,如果是,则将state+1,返回ture。
5、为什么不唤醒所有阻塞的线程,而是只唤醒头节点的next节点?
  在链表中遍历唤醒所有线程效率低O(N),而且根据链表中的存储顺序进行唤醒效率高O(1),也比较公平。

6、为什么同步队列头节点为空节点?
     头节点表示获取到锁的线程记录,为了GC回收方便,所以所有头节点都变为空。已经在aqs类中已经记录绑定获取到锁的线程,所以head结点直接设置为null,防止浪费空间内存。

一、Lock底层实现

  Lock底层基于CAS+AQS组合实现。先CAS自旋,自旋失败使用AQS。
  CAS:CPU底层实现,采用预值比较交换的机制。

二、使用LockSupport操作线程挂起与唤醒  

  LockSupport底层基于UnSafe类,调用c代码,将线程变为阻塞/唤醒。
  线程挂起:AQS先通过CAS将没有获取到锁的线程添加到阻塞队列,再通过LockSupport.park()将线程变成阻塞,类似Synchronized锁对象的wait()方法。
  线程唤醒:AQS先获取需要唤醒的线程节点(头节点的next节点),然后通过LockSupport.unpark(Thread thread)将线程唤醒。

三、AQS介绍

  AQS(AbstractQueuedSynchronizer):抽象同步队列类,底层基于CAS和LockSupport实现,通过资源变量State+AQS的同步管理队列(FIFO),实现了线程抢占资源的管理。 
  state:锁的状态;
  ownerThread:持锁线程;
  Node head,tail  双向链表头节点、尾节点  --prev、next、thread、awaitStatus;
  park() 未获取到资源,将线程封装成node节点,存放在双向链表(同步队列)中,然后使用LockSupport.park()阻塞线程 。awaitStatus = 0;
  unpark() 线程释放资源,state = state-1。如果state=0,将ownerThread设为null,当前节点node设为头节点,node.prev = null,thread = null,awaitStatus = -1等;
  await() 将当前线程存放至单向链表(阻塞队列),释放锁,使state设为0,使用LockSupport.park()将当前线程变为阻塞状态,awaitStatus = -2;
  signal():先采用CAS将线程线程状态awaitStatus由-2变为0,再将该线程追加到同步队列(双向链表)中。如果该线程获取到锁,修改节点状态awaitStatus=-1 。
  AQS是一个抽象类,主要是通过继承的方式来使用,它本身没有实现任何的同步接口,仅仅是定义了同步状态的获取以及释放的方法来提供自定义的同步组件。

三、Lock锁的实现原理

1)获取锁

2)重试获取锁&加入到阻塞队列

3)自旋重试或重入state+1

4)阻塞

   ① 首先将未获取到锁的线程放入队列    

     如果线程没有抢到锁,会放到阻塞链表的最后一个节点位置。该链表数据结构为FIFO队列,所以AQS队列遵循先进先出原则。
     阻塞链表的头节点虽不为null,但为空值节点,不关联任何线程。

   ② 然后使用LockSupport.park()阻塞队列中的线程。

5)唤醒

  为什么不唤醒所有阻塞的线程,而是只唤醒头节点的next节点?
  答:在链表中遍历唤醒所有线程效率低O(N),而且根据链表中的存储顺序进行唤醒效率高O(1),也比较公平。

  当唤醒的线程获取到锁,会将当前线程节点设置为head节点(thread=null,prev=null),即变为空节点,且设置原头节点.next=null,这样便于GC回收原头节点。

四、AQS底层如何实现公平与非公平锁

1、公平锁FairSync:严格遵守请求的

  每一个线程获取锁的情况下,首先会判断,该线程是否为头节点的next节点,如果是,直接去获取锁;如果不是,则放入链表最后位置。
  当持锁线程释放锁,会唤醒链表头节点的next节点,而不是唤醒所有线程,考虑因素(效率高、公平)。

2、非公平锁NonfairSync:通过竞争获取锁,效率高(唤醒锁的时候是公平的)

  每一个线程获取锁的情况下,会进行一次重试,如果成功,即获取锁成功,执行代码;如果失败,则放入链表最后位置。
  当持锁线程释放锁,会唤醒链表头节点的next节点,而不是唤醒所有线程,考虑因素(效率高、公平)。

  非公平锁的效率更高,没有特殊要求的情况下使用非公平锁。
  如:公平锁中,线程1释放锁后,需要唤醒其他线程获取锁,执行代码。如果线程1释放锁后,线程2进来,直接争抢锁,效率更高,毕竟唤醒锁的成本是比较高的。

五、AQS如何唤醒正在等待的线程

  当持锁线程释放锁,会唤醒链表头节点的next节点,而不是唤醒所有线程,考虑因素(效率高、公平)。

六、AbstractQueuedSynchronizer常用属性

1)Node head 头节点

2)Node tail    尾节点

3)Thread  thread  记录没有抢到锁的线程(当前node封装的线程)

  采用双向链表记录没有抢到锁的线程
  在AQS中没有抢到锁的线程,放在链表中最后一个位置(头节点是空的,没有任何属性) 

4)Int  state   拿到锁的线程状态

  0 没有任何线程获取到锁
  1 已经有一个线程获取到锁
  2、3...  说明该获取到线程可能重入了(Lock锁具有可重入性)。每调用lock锁的方法一次,state加1。调用unlock方法一次,state减1,完全释放锁的情况下state=0.

4)Int waitStatus 阻塞线程的状态

  值为1,表示当前的线程被取消;
  值为0,表示当前节点等待获取锁。
  值为-1,表示等待持锁线程释放锁资源后唤醒当前节点;
  值为-2,等待condition唤醒;
  值为-3,工作于共享锁状态,需要向后传播,比如根据资源是否剩余,唤醒后继节点;

七、AQS中头节点为什么为空?

  头节点表示获取到锁的线程记录,为了GC回收,所以所有头节点都变为空。
  已经在aqs类中已经记录绑定获取到锁的线程,所以head结点直接设置为null,防止浪费空间内存。

八、AQS中的Condition源码解读

1、队列(链表)

  等待队列:用于存放在lock锁中调用await方法,当前线程会变为阻塞状态,同时会释放锁 单向链表存放
  同步队列:用于存放没有竞争到锁,采用双向链表存放。

2、await方法阻塞

  1)存放当前线程至condition单项链表
  2)释放锁,使状态为0,唤醒其他线程获取锁。
  3)将当前线程变为阻塞状态(waitStatus=-2)

3、signal方法唤醒

  condition.signal();    只唤醒单项链表中第一个节点。 
  condition.signalAll();  唤醒所有节点
    获取到头节点,循环遍历并唤醒所有节点线程,去CAS参与锁的竞争,
  1)采用CAS将线程线程状态由-2变为0
  2)再将该线程追加到同步队列(双向链表)中,并修改状态为-1             

   3)释放锁的时候唤醒同步队列中的线程

九、基于AQS手写CountDownLatch

思路:

  1、设置aqs类中的状态(state) 
  2、调用await方法,让当前线程变为阻塞
  3、调用countDown方法时,state-1,当state=0时,唤醒阻塞的线程。


http://chatgpt.dhexx.cn/article/A0gUhl3E.shtml

相关文章

AQS原理

AQS是一个构建锁和同步器的并发框架,是AbstractQueuedSynchronizer的缩写,常见AQS实现的同步器框架有ReentrantLock,Semaphore,Latch,Barrier,BlockingQueue等多种多线程访问共享资源的同步器框架,AQS是一种依赖状态(state&#x…

AQS

AQS 简介AQS原理分析AQS原理概览AQS对资源的共享方式AQS低层使用了模板方法模式 AQS组件总结 简介 AQS的全称为(AbstractQueuedSynchronizer),这个类在java.util.concurrent.locks包下面。 AQS是一个用来构建锁和同步器的框架,使…

Java技术之AQS详解

AbstractQueuedSynchronizer 简写为AQS,抽象队列同步器。它是一个用于构建锁和同步器的框架,许多同步器都可以通过AQS很容易并且高效的构造出来,以下都是通过AQS构造出来的:ReentrantLock, ReentrantReadWriteLock A…

(面经总结)一篇文章带你完整复习 Java 中的 AQS

文章目录 一、什么是AQS二、AQS的原理三、state:状态四、AQS共享资源的方式:独占式和共享式一、什么是AQS AQS(Abstract Queued Synchronizer)是一个抽象的队列同步器,通过维护一个共享资源状态(Volatile Int State)和一个先进先出(FIFO)的线程等待队列来实现一个多线…

AQS详细大分解,彻底弄懂AQS

AQS深入分析总结 AQS 很久之前便写了这篇文章,一直没有时间发出来,文章如果有写的不好的地方,欢迎大家能够指正,下面开始详细分析介绍,希望大家能够耐心读下去,肯定会受益匪浅的,AQS是Java JU…

AQS详解

AQS是AbstractQueuedSynchronizer的简称。AQS提供了一种实现阻塞锁和一系列依赖FIFO等待队列的同步器的框架,如下图所示。AQS为一系列同步器依赖于一个单独的原子变量(state)的同步器提供了一个非常有用的基础。子类们必须定义改变state变量的…

一文让你彻底搞懂AQS(通俗易懂的AQS)

一文让你彻底搞懂AQS(通俗易懂的AQS) 一、什么是AQS AQS是一个用来构建锁和同步器的框架,使用AQS能简单且高效地构造出应用广泛的大量的同步器,比如我们提到的ReentrantLock,Semaphore,其他的诸如ReentrantReadWriteLock&#x…

什么是AQS

AQS ( Abstract Queued Synchronizer )是一个抽象的队列同步器,通过维护一个共享资源状态( Volatile Int State )和一个先进先出( FIFO )的线程等待队列来实现一个多线程访问共享资源的同步框架。 一、AQS…

泛函分析之变分法

泛函数 以上截图来自于《变分法简介Part 1.(Calculus of Variations)》 变分法 研究泛函极值的方法就是所谓变分法。 以上截图来自于《最速降线的数学模型—变分法》 欧拉-拉格朗日方程

mathematica变分法和样条插值求解最小旋转曲面

mathematica求解最小面积旋转曲面 做你没做过的事叫成长,做你不愿做做的事叫改变,做你不敢做的事叫突破。—— 巴菲特 问题描述: 在一条直线的同一侧有两个已知点,试找出一条连接这两点的曲线,使这条曲线绕直线旋转所…

变分法模型的运用:生产设备的最大经济效益

上一节介绍了 动态优化模型/ 变分法 的基本思想,本节将一个变分法的运用。 目录 1 问题分析与假设 2 模型构造 3 模型求解 变分法习题 某工厂购买了一台新设备投入到生产中。一方面该设备随着运行时间的推…

简述变分法在泛函极值问题中的应用

此文主要有两部分内容,一部分是泛函的一些基本概念;第二部分是变分法在研究泛函极值问题中的应用。 第一部分 泛函 泛函是函数概念的一种扩充,函数描述的是从数到数的对应关系,从自变量到因变量的一种对应关系;而泛函…

变分法(欧拉 - 拉格朗日)和梯度下降求泛函最优解

泛函的简单理解: 是的变量, 这样的就叫泛函 . 加个积分,这样的就叫积分泛函 . 欧拉 - 拉格朗日 (E - L) 公式: 定义一个能量泛函如下: 我们的目的是找到能使 取到极值的时候 的取值,所以我们就假设 就…

第二章-最优控制中的变分法(经典变分法或古典变分法)1

是《最优控制理论与应用(邵克勇,王婷婷,宋金波)》的读书笔记,相比于其他的书,选择这本书的理由是页数少,能读完。解学书的《最优控制理论与应用》看目录感觉很全,但是太厚了,感觉看不完。 虽然…

变分法理解1——泛函简介

变分法是处理泛函的数学领域,和处理函数的传统微积分相对。 对泛函求极值的问题称为变分问题,使泛函取极值的函数称为变分问题的解,也称为极值函数。 传统的微积分中的一个常见的问题是找到一个 x x x 值使得 y ( x ) y(x) y(x) 取得最大值…

变分法证明两点之间线段最短

传送门https://zhuanlan.zhihu.com/yueaptx 变分法简介Part 1.(Calculus of Variations) Dr.Stein 计算力学 ​关注他 283 人赞了该文章 泛函数 (Functionals) 简而言之,泛函数是函数的函数,即它的输入是函数,输出…

最优控制理论 一、变分法和泛函极值问题

变分法是最优控制问题的三大基石之一,下面讨论一些变分法的常用理论。 1. 性能指标泛函 无约束最优控制问题,若固定起止时间,两端状态固定,即 x ( 0 ) x 0 , x ( t f ) x f , t ∈ [ 0 , t f ] x(0)x_0, x(t_f)x_f, t\in[0,t…

[变分法介绍]优美的旋轮线:最速下降线问题,通过费马光学原理的初等证明

[变分法介绍]优美的旋轮线:最速下降线问题,通过费马光学原理的初等证明 变分法 费马光学原理最速下降线问题旋轮线旋轮线最速下降性质的证明一些旋轮线及变形参考书目:1696年约翰伯努利在写给他哥哥雅克布伯努利的一封公开信中提出了如下的“捷线”问题:设想一个质点沿连接…

深入浅出解析变分法——一种常用的数学方法

前言:笔者从事图像处理行业,总是接触到变分法这个概念,一直没有很深入的去理解这个概念,同时我看其他大佬的博文也比较糊涂,因此最近花了一些时间好好梳理了这部分数学知识。文章共3部分,主要是对变分法的解…

变分法:在图像处理中的应用(一)

前言 最近学习稠密重建的相关知识,发现变分法通常作为一个平滑的正则项出现在残差平方和的损失函数中。而图像处理中又经常出现这类最小损失函数的优化问题,如图像分割、稠密光流、稠密重建等等,这些优化问题中都有可能涉及到变分法。因此&am…