ReentrantLock-AQS加锁过程
2022-01-14 22:01:21 39 举报
ReentrantLock-AQS加锁过程
作者其他创作
大纲/内容
true
false
prev
next
获取到锁,设置当前线程持有锁
NonfairSync#lock
tail尾节点
将当前线程封装的Node节点放入队列尾
Node:n1thread:nullprev:nullnext:n2
Node:n2thread:t2prev:n1next:n3
队列示意图
park逻辑
getState() == 0
aqs队列(链表)
Node:n2thread:t2prev:n1next:null
方法返回false,获取锁失败
尝试获取锁,如果获取不到就等待
非公平锁
2
当前是第一个等待的线程,需要初始化队列并将当前线程加进去
acquireQueued逻辑
否,表示锁被占有了
p == head判断当前节点的前节点是否是head节点,即判断当前节点是否是第一个排队的节点
初始化示意图
tryAcquire
源码
方法返回true,尝试获取锁成功
公平锁和非公平锁逻辑大致相同,只不过在尝试获取锁的tryAcquire方法中会先判断下是否有等待的节点,有就直接获取锁失败
FairSync#lock
enq方法
return false
ReentrantLock
重新设置state的值(线程占有锁)重入锁
false循环
判断占有锁的线程是否是当前线程
尝试获取锁失败,会执行排队逻辑addWaiter方法
sync继承了AbstractQueuedSynchronizer。所以又叫做AQS嘛
结束阻塞
head头节点
尝试获取锁流程
获取到锁,返回
否
public void lock() { sync.lock(); }
排队逻辑
Node:n3thread:t3prev:n2next:null
cas获取锁
return true
这里是设置prev节点的waitState=-1,而不是当前节点。-1表示阻塞状态。因为线程调用park之后,无法设置当前线程的这个状态,而在park之前设置,可能导致不一致,所以设置的都是上个节点
公平锁
设置pred.waitStatus=-1
头节点,也叫哨兵节点
parkAndCheckInterrupt()线程阻塞
重新判断是否轮到当前节点
流程
是
lock
获取到锁。将当前节点设置为head节点,之前的head节点没有引用会回收
pred.waitStatus>0
如果acquireQueued方法能正常返回,就代表获取到锁,否则会一直阻塞,直到获取到锁。
将当前线程封装成一个Node节点放入队列中。tail表示队尾1、队尾不为空(即等待队列中已经存在等待的线程),将当前线程加到队列尾2、队尾为空(即当前线程是第一个需要等待的线程),执行enq方法进行初始化
tryAcquire方法执行流程
current == getExclusiveOwnerThread()重入锁的逻辑
这里个人觉得设计得很巧妙,用了for(;;)。流程: 首先判断node的前节点是否是head节点,如果是,表示当前是排最前面的线程,就尝试获取一下锁,如果锁获取成功,就重新维护下链表,将head设置为当前节点,并将head节点的next设置为null,这样当前节点就成了新的head节点,之前的head节点没有了引用就会被回收; 如果没有获取到锁或者是node节点的前节点不是head节点,就会在判断前节点的waitStatus属性是否是SIGNAL状态,如果是,执行park操作,线程阻塞;如果不是: 1、== 0,设置为SIGNAL 2、ws > 0,当前节点的前节点被取消了,会重新维护链表(进入第二次for(;;))
tryAcquire方法的执行逻辑
获取锁成功,设置当前占有锁线程exclusiveOwnerThread的值为当前线程
abstract void lock();
tryAcquire方法会尝试获取锁,以下几种情况会导致获取锁失败:1、锁已经被其他线程占有2、锁没有被占有,但是当前线程需要等待(公平锁的情况)3、cas失败属性c(state,表示锁的状态,1代表被占有,0代表没被占有)。公平锁和非公平锁的区别:非公平锁上来直接尝试获取,而公平锁还会通过hasQueuedPredecessors方法判断等待队列中是否有线程排队。
acquire(1)
需要做的事情是初始化等待队列,并将当前线程加进去
1
是,表示锁没被占用
pred != null
acquireQueued
> 0,只能是==1,即CANCELLED状态,表示prev节点已经取消,此时需要重新维护链表。
pred.waitStatus == Node.SIGNAL(-1)
0 条评论
下一页