AQS源码解析
2021-12-01 23:38:42 11 举报
AQS源码解析-非公平锁
作者其他创作
大纲/内容
公平锁
将当前线程封城成Node对象,并加入排队队列中。根据排队队列是否执行过初始化,执行1、2不同处理逻辑。1、表示排队队列不为空,即之前已经初始化了,此时只需将新的Node加入排队队列的末尾即可。2、表示排队队列为空,需执行队列初始化。enq会初始化一个空的Node(哨兵),作为排队队列的head。然后将需要排队的线程,作为head的next节点插入。
next
acquireQueued方法详解
public void lock() { sync.lock(); }
tryAcquire(arg)
true
返回false
FairSync.lock()
false
Sync extends AbstractQueuedSynchronizer
参数1:其前面节点prev参数2:准备执行park操作的节点
private final boolean parkAndCheckInterrupt() { LockSupport.park(this); return Thread.interrupted(); }
final void lock() { acquire(1); }
head
Node:n1thread=nullprev=nullnext=n2
执行park逻辑
enq()
lock()
prev
addWaiter
tail
Node:n2thread=t2prev=n1next=n3
parkAndCheckInterrupt 线程进入阻塞状态
acquireQueued
阻塞结束
返回Node
acquire(1)
设置pred.waitStatus=-1
表示获取到了锁,线程逐层返回,加锁过程结束
p == head?表示查询node是不是第一个排队的
返回false表示线程没有获取锁
sync.lock()
获取到锁,设置持有锁的线程为当前线程并返回。
表示获取到锁:1、node设置为头节点2、释放之前的头节点这个逻辑完成后,node节点就不再排队了(获取到锁的线程,不会再排队队列中)
acquireQueued返回,表示线程获取到锁,线程逐级返回,加锁过程结束。
AQS有两个属性head和tail,分别来保存aqs队列的头尾结点,初始化时,这两个属性是null,当有一个线程排队,队列如下:
两种情况会导致阻塞结束:1、持有锁的线程,释放锁后,将这个线程unpark了,此时该线程,一定在队列的对头(不包括head节点);2、线程interrupt了(注意,在外部interruput这个线程,不会抛出interruptException,这一点和sleep wait阻塞不一样)
返回true
addWaiter详解
Node:n2thread=t2prev=n1next=null
非公平锁
注意这里设置的pred节点,而不是node节点waitState,-1表示节点处于阻塞状态。为什么每个线程进入该方法后,修改上一个节点的waitState,不是自己的?因为线程调用park后,无法设置自己的这个状态,都在调用park前设置,存在不一致的问题,所以每个node的waitState都在后继节点加入时设置。
aqs队列(链表)
再次循环判断
这部分,是node加入队列后的处理逻辑,这里会有一次自旋,尝试获取锁,如获取不到,才调用park,阻塞自己。
CAS获取锁
NonfairSync.lock()
false(即==0)
false再次循环判断
没有其他线程在排队,调用enq构造队列,并将Node加入到队列
pred.waitStatus>0?
prev!=null
tryAcquire
abstract void lock()
队列已经初始化了,此时只需将新的Node加入排队队列的末尾即可。
ReentrantLock
Node:n3thread=t3prev=n2next=null
pred.waitStatus==Node.SIGNAL?
shouldParkAfterFailedAcquire详解
0 条评论
下一页