AQS源码
2021-05-17 14:09:20 28 举报
AQS(AbstractQueuedSynchronizer)是Java并发包中的一个抽象类,它为实现依赖于先进先出 (FIFO) 等待队列的阻塞锁和相关同步器(如信号量、事件等)提供了一个框架。AQS 维护了一个状态值和一个等待队列,当线程请求资源时,如果资源已被占用,则将线程加入等待队列;当资源释放时,从等待队列中唤醒一个线程来获取资源。AQS 的主要方法包括 acquire()、release()、tryAcquire()、tryRelease() 等,这些方法可以在自定义同步器中实现具体的同步逻辑。
作者其他创作
大纲/内容
NonFairSync#lock()
3
tail
false
pred ! = null
Node:n2thread = t2prev = n1next = n3
Node:n2thread = t2prev = n1next = null
true
返回 false
执行排队
false (即 == 0)
next
返回 true,表示获取到了锁,线程逐级返回。加锁过程结束
这部分,是 node 加入队列后的处理逻辑,这里会有一次自旋,尝试获取锁,获取不到,才调用 park,阻塞自己。
shouldParkAfterFailedAcquire 方法详解
再次循环判断
阻塞结束
acquireQueued 返回,表示线程获取到锁,线程逐级返回,加锁过程结束。
head
acquireQueue 方法详解
返回 false,表示线程没有获取到锁
sync 继承了 AbstractQueuedSynchronizer
prev
执行 park 逻辑
aqs 队列(链表)
公平锁
fasle
返回 true
Node:n3thread = t3prev = n2next = null
false,执行上图步骤2
parkAndCheckInterrut 线程进入阻塞状态
addWaiter
这个队列表示,在线程 C 进入之前,已经有一个线程在排队获取锁了。
这个队列表示,在节点 n2 插入队列之前,没有其他节点在排队。注意,当前持有锁的线程,永远不会处于排队队列中。这是因为:当一个线程调用 lock 获取锁时,只有两种情况:1) 锁处于自由状态 (state==0) ,且它不需要排队,通过 cas 立刻获取到了锁,这种情况,显然它不会处于排队队列中;2)锁处于非自由状态,线程加入到了排队队列的队头 (不包括 head ) 等待锁。将来某一时刻,锁被其他线程释放,并唤醒这个排队的线程,线程唤醒后。执行 tryAcquire. 获取到了锁,然后重新维护排队队列,将自己从队列移出 (acquireQueued 方法)。所以,不管哪种情况,持有锁的线程,永远不会处于排队列表中。
设置 pred.waitState = -1
尝试获取锁,如果获取不到,排队(阻塞当前线程)
addWaiter 详解
pred.waitState >0?
AQS 有两个属性 head 和 tail ,分别用来保存 aqs 队列头尾节点。初始化时,这两个属性都是 null,当有一个线程排队,队列如下,
false 再次刷新
返回 node
enq 方法
abstract void lock()
这个方法,接收两个Node 对象参数:参数2 是准备执行 park 操作的节点 node,参数1是其前辈节点 pred.
p == head ?表示查询 node 是不是第一个排队的
sync.lock()
> 0 ,只能 == 1,即 CANCELLED。表示 node 的前辈节点,已经取消了,此时将链表关系重新维护下:即将其前辈节点从队列移出
CAS 获取锁
pred.waitState == Node.SIGNAL(-1)?
FairSync#lock()
acquire(1)
队列尚未初始化,调用 enq 方法该方法生成一个空的 Node 对象(new Node(),右图粉色部分),插入到 aqs 队列头部,然后将参数 node ,作为后继节点,插入队列,方法执行完毕,队列如右图。
队列已经初始化,将 node 加入到队列末尾,加入后队列可能如下
tryAcquire
acquireQueued
表示获取到锁:1. node 设置为头结点2、释放之前的头结点这个逻辑执行完成后,node节点就不再排队了(获取到锁的线程,不会在排队队列中了)
没有其他线程在排队,调用 enq 构造队列,并将 node 加入队列。
成功获取到锁,设置锁持有线程为当前线程。返回
lock
ReentrantLock
非公平锁
Node:n1thread = nullprev = nullnext = n2
0 条评论
回复 删除
下一页