type
status
date
slug
summary
tags
category
icon
password
😀
这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
 

📝 基本介绍

AQS(队列同步器),简称同步锁框架。
同步器是实现锁的关键,利用同步器将锁的语义实现,然后在锁的实现中聚合同步器。
AQS会将请求获取锁失败的线程放入一个队列的尾部:等待获取锁的线程全部处理阻塞状态。当前线程执行完毕(释放锁)后,会激活当前线程的后继节点。
  1. 采用模板模式,tryacquire(), release()等方法需要子类覆写。实现了算法主体框架,供外部调用,里面会调用原语操作和钩子操作
  1. 原语操作:即定义的抽象方法,子类必须重写
  1. 钩子操作:与原语操作类似,也是供子类重写的。区别是钩子可以重写也可以不重写,如果不重写默认使用父类的
  1. 一般的lock类都实现Lock接口的,而内部会继承AQS,因为Lock是面向使用者,而AQS这个框架是锁的实现框架,使用者一般不会关注AQS

CLH队列

介绍

CLH锁其实就是一种基于队列(具体为单向链表)排队的自旋锁,由于是Craig、Landin和Hagersten三人一起发明的,因此被命名为CLH 锁,也叫CLH队列锁
简单的CLH锁可以基于单向链表实现,申请加锁的线程首先会通过 CAS操作在单向链表的尾部增加一个节点,之后该线程只需要在其前驱节点上进行普通自旋,等待前驱节点释放锁即可
由于CLH锁只有在节点入队时进行一下CAS的操作,在节点加入队列之后,抢锁线程不需要 进行CAS自旋,只需普通自旋即可。因此,在争用激烈的场景下,CLH 锁能大大减少CAS操作的数量,以避免CPU的总线风暴
声明一个node节点,locked是自身,myPred是前一个node节点的引用(组成一个单向链表),因为获取锁的线程,可能是多个,后节点会不断的自旋(普通自旋)自己的myPred指向的前一个node的Locked属性,当这个属性探测得到是false的时候,说明前驱节点已经释放了锁,自己应该去抢锁了
notion image
 

方法

重写方法

方法名称
描述
boolean tryAcquire(int arg)
独占式获取同步状态,成功返回true,失败返回false
boolean tryRelease(int arg)
独占式释放同步状态,成功返回true,失败返回false
int tryAcquireShared(int arg)
共享式获取同步状态,获取成功则返回值>=0
boolean tryReleaseShared(int arg)
共享式释放同步状态,成功返回true,失败返回false
boolean isHeldExclusively()
判断同步器是否在独占模式下被占用,一般用来表示同步器是否被当前线程占用

重写模板方法

子类需要重写该方法,AQS中提供的模板方法才可以正常调用
方法名称
描述
void acquire(int arg)
独占式获取同步状态,该方法会调用子类重写的tryAcquire(int arg),如果tryAcquire返回true则该方法直接返回,否则先将当前线程加入同步队列的尾部,然后阻塞当前线程
void acquireInterruptibly(int arg)
当线程获取同步状态失败被阻塞后,可以响应中断,收到中断后将会取消获取同步状态
boolean tryAcquireNanos(int arg, long nanosTimeout)
在acquireInterruptibly的基础上加了超时限制,如果在超时时间内获取到同步状态返回true,否则返回false
boolean release(int arg)
独占式释放同步状态,该方法会在释放同步状态后将第一个节点(对应刚刚释放同步状态的线程)的后继节点对应的线程唤醒
void acquireShared(int arg)
共享式获取同步状态,该方法会调用子类重写的tryAcquireShared(int arg),如果tryAcquireShared返回true则该方法直接返回,否则先将当前线程加入同步队列的尾部,然后阻塞当前线程
void acquireSharedInterruptibly(int arg)
当线程获取同步状态失败被阻塞后,可以响应中断,收到中断后将会取消获取同步状态
boolean tryAcquireSharedNanos(int arg, long nanosTimeout)
在acquireSharedInterruptibly的基础上加了超时限制,如果在超时时间内获取到同步状态返回true,否则返回false
boolean releaseShared(int arg)
共享式的释放同步状态

State

volatile关键字进行修饰 0=初始值,没有被线程占用 1=锁已经被抢占(抢占之后调用unlock方法会释放锁,将state=1重置为0
1的情况说,说明锁被重入了。举例子:该值等于3,说明已经被重入了3次,最后递减state的时候也应该递减3次,重置为0的时候表示已经释放了锁

Node

 

AQS的Node

FIFO队列

notion image

AQS结构

notion image
 

AQS执行流程

node节点会通过CAS算法把自己变成读取到尾部node节点的下一个节点,不成功的继续自旋读取最新的尾节点再CAS
notion image
notion image
 

acquire 方法

notion image

acquireQueued 方法

1. node.predecessor 找到前一个节点,如果是head节点的话,就CAS获取锁(业务执行块的话,前面锁释放了,这时候尝试获取一些) 2. 如果获取成功的话,当前节点设置头节点,将原来的head节点设置为null 3. 如果1 和 2 不成功,走 shouldParkAfterFailedAcquire,检查自己是不是应该park,如果park之后,交给上一个线程去唤醒

release 方法

释放锁的时候,会唤起head节点的下一个节点,当后置节点拿到锁之后,会把head指向自己
 
 
 

🤗 总结归纳

📎 参考文章

 
💡
有关文章的问题,欢迎您在底部评论区留言,一起交流~