ReentranLock底层原理--AQS图解
2022-06-07 13:10:36 0 举报
剖析AQS底层原理,熟悉ReentranLock加锁解锁逻辑。
作者其他创作
大纲/内容
是队列中有等待线程
否
setExclusiveOwnerThread()
release(1)
分支流转
未获取到锁
NonfairSync(非公平锁)
阻塞当前线程,返回当前线程中断状态parkAndCheckInterrupt()
LockSupport.unpark(s.thread)解除s.thread线程的阻塞
调用解锁
设置新的statesetState(c)
子类
总结:入CLH队列后尝试获取锁,先判断是否为第一个节点。是的话尝试获取锁,未获取到锁或不是第一个节点且非阻塞会进行自旋,重复尝试获取锁,如果阻塞,返回当前中断状态。
内部类
判断CLH队列中是否存在等待的线程!hasQueuedPredecessors()
添加失败自旋保证队列全部入队
调用加锁
free = false
1.先尝试获取锁
是否获取到锁
逻辑
新的statec == 0 ?
设置当前线程独占锁setExclusiveOwnerThread(Thread.currentThread())
设置当前拥有独占访问权的线程
acquire(1)
selfInterrupt()->Thread.currentThread().interrupt()设置打断标记,将中断信号外传
添加成功
将当前线程对应的Node对象从CLH队列移除,并重新设置队列
return free
总结:线程加锁失败后,将当前线程添加到CLH队列,如果队列中有等待线程,将当前队列添加到队列尾部,如果没有,先检查是否初始化,初始化了就添加到队列尾部,未初始化就先初始化一个队列再添加注意:CLH队列会维护一个前驱节点,当前节点的pred == null 才是头节点,目的是方便判断,进行状态转换时也会简单许多
模板方法模式
AbstractQueuedSynchronizer
获取当前拥有独占访问权的线程
实现类
AbstractOwnableSynchronizer
当前节点是否为队列中的第一个节点?node.predecessor() == head ?
类/接口
判断是否是当前线程占用current == getExclusiveOwnerThread()
设置当前占用访问权线程为nullsetExclusiveOwnerThread(null)
getState()
ReentrantLock(可重入锁)
是否应该阻塞线程?shouldParkAfterFailedAcquirespan style=\"font-size: inherit;\
state != 0被线程占用此时公平锁与非公平锁实现逻辑一致
否自旋重复执行for循环尝试获取锁
addWaiter(Node.EXCLUSIVE)
总结:如果锁没有被占用:1.非公平锁直接尝试获取锁,2.公平锁先判断是否有等待线程,没有再尝试获取锁。3.如果被线程占用,则判断是否是当前线程占用。如果是就修改state状态,如果不是直接返回false执行后续代码。
计算新的statec = getState() - releases如果不是当前线程占用锁抛异常
初始化CLH队列compareAndSetHead(new Node())tail = head
2.添加当前线程到CLH队列中EXCLSIVE独占模式
tryRelease(arg) ?
超类
获取锁成功
返回当前线程对应的Node对象
自我中断设置打断标记
是
state == 0没有被线程占用
Node pred = tail同步队列中是否有等待线程 pred != null ?
非公平锁(不判断直接尝试获取锁)
获取锁失败
getExclusiveOwnerThread()
Node h = head判断(头节点不为空,且waitStaaatus不为0)h != null && waitStatus != 0
1.粉色容器:类/方法关系图解2.红色容器:加锁逻辑图解3.灰色容器:解锁逻辑图解
图例
Node s = node.next;if (s == null || s.waitStatus > 0)(异常中断,废弃)赋值s = null,循环从尾部节点开始遍历,找到最前面的waitStatus<=0的线程,赋值s = t;
成功
返回true
应该阻塞
失败
FairSync(公平锁)
返回当前线程是否中断
unparkSuccessor(h)唤醒节点的后继节点(如果存在)
false
free = true
尝试获取锁成功返回TRUE
返回false
添加当前线程到队列中enq(Node)
Sync
lock()
!tryAcquire(arg)
是否获取成功
尝试获取锁失败返回FALSE继续执行后续代码
true当前线程不再占用锁
(类中)方法
公平锁
修改Sync对象State的值nextc = c + acquiressetState(nextc)
总结:1.调用unLock()时,首先判断当前线程是否为独占拥有线程 if (Thread.currentThread() != getExclusiveOwnerThread()) 不是则抛出异常。2.判断状态state值是否等于缓存状态值。即二者差是否为0。为0则设置当前占用访问权线程为null,改state为0,返回true。3.判断CLH队列头节点是否为空且waitStatus不为0,然后唤醒后续节点。将头节点后的waitStatus不大于零的pred指向head,然后释放锁。
将当前队列添加到CLH队列尾
方法中的逻辑
true
Lock
队列是否初始化tail == null ?
3.在CLH队队列中尝试获取锁
获取到锁
unLock()
公平锁与否?
尝试获取锁tryAcquire()
添加成功与否
Sync锁是否被线程占用?(state = 0?)
不应该阻塞自旋重复执行for循环尝试获取锁
0 条评论
回复 删除
下一页