ReentrantLock原理
2022-05-23 14:57:27 0 举报
登录查看完整内容
ReentrantLock原理
作者其他创作
大纲/内容
ws = -1
当前持有锁线程(当前获取锁的线程是谁)
否;尾部节点不为空,说明此时有人在排队往队列尾部添加当前Node
执行unparkSuccessor(head)唤醒CLH队列中某个节点对应线程的阻塞
设置标识boolean failed = true;boolean interrupted = false;
nonfairTryAcquire(1)
检查head头节点状态是否为-1(当前排队第一的节点能否正常唤醒)h != null && h.waitStatus != 0
Thread.interrupt()线程中断唤醒线程
font color=\"#f57f17\
font color=\"#ffb74d\
尾部节点不为空往队列尾部添加当前Node
ws 0
将state+1int nextc = c + acquires;
获取到锁
为空
再次执行for(;;)循环执行tryAcquire(1)再次尝试获取锁资源
结束
尝试获取锁成功!
帮助GC垃圾回收p.next = null; // help GC修改标识 failed = false;
重新设置-1之后锁状态值setState(c)
setState(c + acquires)重新设置state值锁重入成功
前驱节点是否有效 (ws = pred.waitStatus) == Node.SIGNAL 或或者 (上一节点有效(但是不为-1) 则将上一节点状态尝试设置为(-1)有效状态 并且 前驱点线程不为null)ps:当前操作为了保证前驱节点为有效节点
如果+1后state小于0则超出可重入最大值,抛出异常throw new Error(\"Maximum lock count exceeded\")
设置尾部节点等于头部节点;初始化CLH队列尾节点 = 头节点tail = head
CLH双向队列
(以下逻辑均为非公平锁执行逻辑)
node.waitStatus = Node.CANCELLED将当前节点置为失效节点,因为上边已经排除当前节点所有已失效的前驱节点,当另外的Node也刚好做相同操作时,可以指直接找到当前节点的前驱节点作为前驱节点(因为当前节点也已经置为失效了)
成功
被其他线程占用state != 0
ReentrantLock
ws == 0 或者 ws 0 且不等于 -1
int state
前驱节点为head头节点
进行线程阻塞parkAndCheckInterrupt() LockSupport.park(this)
线程进入阻塞状态此时线程阻塞在此处
状态器:获取锁+1
解锁入口
设置当前节点的上一节点(prev)为尾部节点node.prev = t
出现异常需要将当前节点失效当前代码执行的几率非常低 约等于0cancelAcquire(Node node)
否
return Thread.interrupted()断获取当前中断线程中断状态(true)(获取中断状态会清除中断标识)currentThread().isInterrupted(true)
当前节点是否为tail尾部节点
正常唤解锁唤醒后向下执行
是
非公平锁Class NonfairSync
尾部节点为空,说明当前没有人排队当前Node是第一个
抛出异常throw new IllegalMonitorStateException()
当前线程 是否不等于 当前占用锁资源线程 Thread.currentThread() != getExclusiveOwnerThread()
CAS原子操作失败
获取head头部节点的next后继节点Node s = node.next
获取head头部节点的waitStatus状态int ws = node.waitStatus
true
如果上一节点状态为-1,则状态正常,保证上一节点是有效节点才可以进行线程阻塞
直接unpark唤醒线程LockSupport.unpark(s.thread)
CAS操作成功
尝试将前驱节点的next节点设置为当前节点的next节点(必须是有效的next节点)font color=\"#ff9800\
如果状态小于0,则将head头部节点状态改为0font color=\"#ff9800\
继承
获取当前Node的上一个节点(prev)final Node p = node.predecessor();
修改AQS当前占用线程为null setExclusiveOwnerThread(null)
CAS操作设置当前节点为尾部(tail)节点font color=\"#ff9800\
获取锁资源失败
公平锁Class FairSync
获取AQS锁状态并且将当前值-1int c = getState() - releases
加1后state是否小于0
初始化成功
获取CLH队列尾部节点,并判断尾部节点是否为空;做这一步目的是为了判断当前队列中是否已经有人在排队
健壮性判断当前node是否为null
设置exclusiveOwnerThread属性设置为当前线程,这个属性是AQS的父类提供的 setExclusiveOwnerThread(Thread.currentThread());
判断尾巴节点是否为null(队列是否初始化?)
返回当前节点Node
for(;;)循环
tryAcquire(1)返回true
内部类
属性
Lock()
使用lockInterruptibly加锁时,如果中断唤醒线程则会执行当前代码
执行tryReleaseb style=\
Thread exclusiveOwnerThread
未获取到锁
执行此方法说明tryAcquire()获取锁资源失败,需要将当前线程封装成一个Node,追加到AQS的队列中等待addWaiter(null)
false
从尾部tail节点开始往前遍历找到最靠近head头部节点的 且 waitStatus小于等于0(有效的节点)的Nodeps:等于0的情况主要是针对尾部tiil节点的情况,其他有效节点waitStatus小于0
否;返回false执行入队操作
acquireQueued返回 interrupted = true
获取当前节点下一节点 Node next = node.next
是,说明当前节点为第一个排队的节点
传入上一节点和当前节点;执行返回boolean保证当前节点上一节点为-1font color=\"#ff9800\
return Thread.interrupted()断获取当前中断线程中断状态(false)(获取中断状态会清除中断标识)currentThread().isInterrupted(true)
下一节点不为空 且 下一节点状态有效next != null && next.waitStatus <= 0
拿到锁资源设置head节点为当前节点,将thread,prev设置为null,因为拿到锁资源了,不需要再进行排队head = node; node.thread = null; node.prev = null;
Node node
获取尾巴节点tailNode t = tail
当前节点上一节点是头节点?p == head
将当前节点下一节点指向自己(因为当前节点已经失效了)node.next = node;// help GC
CAS操作失败重复执行for (;;) 循环;直到成功
不是头节点,说明当前节点为中间节点
等于
如果上一节点的waitStatus状态大于0则表示当前节点上一节点已经失效,需要丢弃,直到找到waitStatus小于等于0的节点作为当前节点的上一节点,并且把上一节点的next指向当前节点
如果当前节点前驱节点已经失效,则一直往前找,直到找到最近的有效节点作为当前节点的前驱节点
int c = getState();获取AQS锁状态;如果state为0,代表,尝试再次获取锁资源
AbstractQueuedSynchronizer
AbstractOwnableSynchronizer
执行acquireQueued方法 Throw Exception抛出异常
更改标识为trueinterrupted = true;再次执行for(;;)循环执行tryAcquire(1)再次尝试获取锁资源
执行enq(node)
设置当前节点的上一节点(prev)为尾部节点node.prev = pred
获取当前节点前驱节点,并且判断前驱节点是否已失效(waitStatus > 0)
前驱节点是否为Head头部节点
if (!tryAcquire(arg) && font color=\"#ff9800\
Lock.unLock();release(1)正常解锁唤醒
unLock()
不为空
是尾部节点
执行b style=\
释放锁资源失败
执行tryAcquire(1)再次尝试获取锁资源(state从0-1,锁重入操作),成功返回true,失败返回false
不等于
current == getExclusiveOwnerThread()当前占有锁资源的线程是否是当前线程
tryAcquire再次尝试获取锁资源,如果尝试成功,返回truetryAcquire(1)
CAS操作将当前节点的上一节点waitStatus设置为-1
中断唤醒后向下执行
当前判断逻辑返回true当前线程是由中断信号唤醒,则在获取锁之后需要将中断信号重新加上(原因是因为在中断唤醒之后获取线程中断标识时会将中断标识清除,所以要在此将中断标识重新加上) Thread.currentThread().interrupt();
CAS操作初始化(CAS操作是因为此刻可能存在竞争)头部节点(头部节点没有实际意义)compareAndSetHead(new Node())
继续执行enq入队操作
acquireQueued返回 interrupted = false
说明当前节点为排队第一位的节点,当前节点失效后,则它的后继节点就是排队第一位的节点,所以需要唤醒当前节点的后继节点unparkSuccessor(node)
执行releaseb style=\
静态内部类
已经将node加入到了双向队列中,然后执行当前方法font color=\"#ff9800\
将当前node节点Thread设置为null(因为当前节点不需要再竞争资源了)
没有线程占用state == 0
1、获取上一节点的Node标识int ws = pred.waitStatus;2、判断ws是否为-1if (ws == Node.SIGNAL)
说明现在没有人排队,当前Node为第一个
int c = getState() - releasesc = 0 ?
Static Sycn sync
shouldParkAfterFailedAcquire返回false
node为空,直接结束
锁入口
将之前尾部节点的下一节点(next)设置为当前节点pred.next = node
收藏
0 条评论
回复 删除
下一页