aqs加锁流程图
2021-09-08 18:48:23 1 举报
一张图描述AQS如何加锁的,这里1以ReentrantLock#lock()为入口分析
作者其他创作
大纲/内容
调用NonfairSync#tryAcquire
执行业务
获取当前线程
返回true
是否为首次设置
改变原来tail节点的next节点指针指向传入的node节点
被唤醒后继续自旋
调用NonfairSync#lock加锁
通过pred作为临时节点存储tail
前驱节点为0或Propagate,则cas尝试设置前驱节点为-1
调用tryAcquire尝试获取锁
返回tail节点
调用acquire尝试获取锁
调用tryAcquire再次尝试抢锁
将原头节点清理,便于GC
TheadA
失败
-1
state状态
addWaiter流程
新建Node节点
获取当前的为尾节点
注意,此时的tail节点与head节点是同一个
是
获取前一个节点状态
修改前一个tai节点的next指针指向心节点
cas尝试修改state抢占锁
获取当前节点
返回标记
判断是否为头节点
如果为自己,则说明自己再次获得锁
否
抢锁失败后进入park流程
判断当前持有锁的线程是否为自己
如果当前锁已经被其他线程占用,此流程回直接返回false,if和elseif均不满足条件
创建新的Node。这里的节点创建包含Thread,这里的mode默认为排他锁Node.EXCLUSIVE
返回false
将当前线程park住,也就是挂起
主流程
获取前一个节点
调用Sync#nonfairTryAcquire
state=1
设置头节点为当前节点
设置中断标记为已中断
开始自旋逻辑
首次复制,让头节点和尾节点
tryAquire流程
设置状态为最新的重入次数
设置中断标记,默认未被中断
调用NonfairSync#lock释放锁,并唤醒park的线程
回到自旋流程
设置当前排他锁拥有者为自己
tail
调用addWaiter入队
TheadB
通过cas设置自己节点为新的尾节点
抢锁成功
锁未被抢占,通过cas抢占锁
回到自旋,这一次回走else流程,因因为head节点已准备好
锁重入,就是将获得锁的次数累加,获得多少次,需要释放多少次
state=0
cas设置当前的尾节点为心节点
其他状态
抢占失败
尾节点是否为空
开始下一次自旋
设置当前节点的前驱节点为tail节点
tail节点不为空,说明
注意:这里挂起的线程,还在自旋逻辑中,也就是unpark后会继续自旋抢锁
tail节点为空
是否应该park自己
前驱节点为可唤醒状态
AQS中的头节点,代表当前抢占了锁的线程
api调用lock方法加锁
调用acquireQueued进行park等待唤醒
自旋过程
获取state状态
抢占成功
设置心节点的前驱节点为pred
抢占成功将自己设置为当前拥有锁的线程
设置中断标记
开始自旋
true
0
通过cas设置头节点为新的Node节点
为空,代表首次初始化CLH队列,你如enq方法
代表前驱节点的任务因为自身原因已取消,则开始循环找到一个支持唤醒的节点。重新连接链表
调用LockSupport.park挂起当前节点
acquireQueued流程
等待被唤醒
head
0 条评论
回复 删除
下一页