首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

图+文字的方式带你透析Lock源码

今天公司项目中需要使用Lock锁,所以顺便也看了下源码,同时发表一下自己的理解。

Lock:接口,用于定义锁操作的方法

ReentrangtLock:Lock接口的具体实现类

AbstractQueuedSynchronizer:同步队列

FairSync:公平锁

NonfairSync:非公平锁

锁的结构:

State:大于等于0,等于0则表示没有加锁,大于0表示已加锁

Queue:双向链表,用于保存竞争锁的Thread

OwnerThread:表示加锁成功的线程

Node:节点

Node head:头结点

Node tail:尾节点

以非公平锁为例:有三个线程进行加锁,分别为线程A,线程B,线程C;

lock()方法:

此时线程A最先执行获得锁通过CAS将state的值设置为1,并将OwnerThread设置为线程A。

那么线程B,线程C怎么办?此时调用acquire(1)方法将线程封装为一个Node对象,组成一个双向链表。tryAcquire(arg)方法此时会尝试获取锁,当然肯定是失败的。所以会首先执行addWaiter()方法

将线程加入到双向链表中,将线程B,线程C封装为Node节点,第一次tail肯定是null,所以走enq(node)方法.

enq()方法主要是将Node加入到双向链表中

图示:

线程B先执行enq方法,并返回当前线程

如果线程B已经添加到双向链表中后,返回Node调用acquireQueued方法

进入死循环,如果当前线程是B。

拿到线程B的上一个节点,判断是否等于头节点,并且会重新尝试加锁,这两个条件都为true,才会执行if里面的代码。由于线程A的所以并没有释放,所以这里不会进入

所以进入下一个if

shouldParkAfterFailedAcquire(Node pred, Node node)主要是设置线程

Node节点的上一个节点的waitStatus状态,如果等于-1表示是可唤醒竞争锁的线程

大于0则是异常,其他等于0表示初始值则修改waitStatus等于-1

parkAndCheckInterrupt()阻塞当前线程,也就是说当线程运行到这里的时候阻塞了

unlock()方法:

tryRelease():就是对state值减一,如果state等于0就设置owenrThread

为null,否则就是一个重入锁,只需设置state值就行

unparkSuccessor():拿到头节点,判断waitStatus如果小于0则设置头节点的

waitStatus为0。并拿到头结点的下一个节点,如果下一个节点为null或者节点的waitStatus大于0,则会从链表尾部将这些Node给删除。否则就会唤醒这个节点。

唤醒节点后:又会调用这个acquireQueued()方法,走第一个if进行尝试获取锁,

如果获取锁成功,则将这个节点设置为头节点,原来头节点的next设置为null.

至此,加锁以及释放锁都完了,其实也不是很难。

公平锁与非公平锁的区别:公平锁:lock时期,就必须将其加入到队列中

非公平锁:lock时期,会首先尝试通过CAS更改state的值进行获取锁

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20200726A0P55Z00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券