ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

ReentrantLock加锁解锁过程

2019-11-23 12:57:09  阅读:294  来源: 互联网

标签:加锁 pred 解锁 ReentrantLock t2 t3 t1 方法 节点


公平锁

调用lock方法加锁

 

进入acquire方法获取加锁的许可

 

 进入tryacquire

 

 首先获取当前线程和status状态,status默认为0

假如现在t1线程进入,然后t2线程进入(t2进入时t1还没有释放锁)

if c==0成立,然后判断是否需要排队,调用hasqueuedpredecessors方法

 

 此时的头和尾都是null,此方法返回false,所以上面if(!hasqueuedpredecessors())成立,然后进行cas操作,将status改为1

然后设置持有锁的是当前线程。最后返回true。即t1拿到锁继续执行自己的业务逻辑。。。。

1、如果t2执行lock方法的时候t1已经释放锁,也就不会存在竞争,一次执行。

2、如果t1还没有释放锁,t2也会走上面的代码。

走到tryacquire方法尝试去获取锁的时候肯定失败,因为t1还在占用。方法返回false。

 

 然后会执行addwriter方法进入队列排队。

 

 首先创建一个节点node,节点里包含属性thread,pre,next,是一个双向链表

由于tail=null,所以会走enq方法。

 

 此处无限循环,t==null也成立,首先会先初始化一个新的Node,node里的信息目前为空。然后设置为头部。(其实队列里的头部永远都是一个空节点,空节点的意思是有node对象,只不过里面的thread属性为空)

继续循环,走else,里面的逻辑意思是将自己的节点设置为尾部,空节点设置为头部,然后将空节点的next指向t2节点,t2的pre指向头部的空节点。

最终的关系如下图

 

 

继续走acquirequeued方法

 

 

 1 private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
 2         int ws = pred.waitStatus;//等待状态,默认为0,Node.SIGNAL=-1
 3         if (ws == Node.SIGNAL)
 4             /*
 5              * This node has already set status asking a release
 6              * to signal it, so it can safely park.
 7              */
 8             return true;
 9         if (ws > 0) {
10             /*
11              * Predecessor was cancelled. Skip over predecessors and
12              * indicate retry.
13              */
14             do {
15                 node.prev = pred = pred.prev;
16             } while (pred.waitStatus > 0);
17             pred.next = node;
18         } else {
19             /*
20              * waitStatus must be 0 or PROPAGATE.  Indicate that we
21              * need a signal, but don't park yet.  Caller will need to
22              * retry to make sure it cannot acquire before parking.
23              */
24             compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
25         }
26         return false;
27     }

所以会走到compareAndSetWaitStatus方法将上一个节点的waitstatus改为-1,然后返回false,由于acquirequeued方法里是for(;;)

所以会继续执行shouldParkAfterFailedAcquire,此时的waitstatus=-1,返回true。

这个时候就是所谓的自旋,自旋了两次。

返回true之后根据上面代码的逻辑会走parkandcheckinterrupt方法

 

 

 调用park方法,此时t2开始阻塞。知道t1释放锁。

此时t3如果也进入lock方法。

如果t1还没有释放锁, 和t2同样的逻辑。直到t3也阻塞。

如果此时t1已经释放锁,那么会是t2拿到锁。因为入队之后,会尝试一次加锁许可的过程中,会判断当前节点是不是第一个排队的节点(也就是他的上一个节点是不是头部)

如果是才有资格去获取锁,因为前面还有t2,所以t3会继续排队,t2被唤醒之后,会将自己节点的thread属性赋值null,next指向t3,t3的pre指向t2。

然后把头部指向t2,尾部指向t3。空节点的next=null。

此时最先初始化的那个空节点已经没有任何引用。等待被回收。源码里有体现。

非公平锁

 

 调用lock方法,直接就就行cas操作。

unlock解锁过程

 

 

 

 

 

 

 

 

 简单几步:1、将status设置为0

2、设置持有当前锁的线程为null

 

3、唤醒队列里waitstatus不等于0的节点,调用unpark方法。

 

 

 简单总结:调用lock方法,存在竞争的时候,T2会去入队,首先会初始化一个空节点,t2节点实际上存放的是第二个位置,t3进来的时候继续在后面排队,

 t2和t3都是调用park方法进行阻塞。入队的时候会将前面的节点的waitstatus状态由0改为-1。在调用unlock的时候会将waitstatus不等于0的释放。

 

标签:加锁,pred,解锁,ReentrantLock,t2,t3,t1,方法,节点
来源: https://www.cnblogs.com/hkdpp/p/11917383.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有