32张图带你了解ReentrantLock&AQS源码

1.ReentrantLock加锁过程

reentrantLock的加锁方式是调用lock方法
而真正调用的是sync.lock()
在这里插入图片描述
在这里插入图片描述

sync默认是一个非公平锁NonfairSync (可以插队),与其说是非公平锁,其实是一个非公平队列(也就是AQS)
在这里插入图片描述
在这里插入图片描述

所以加锁的时候真正调用的是下图中的NonfairSync 的lock方法
在这里插入图片描述

1.1加锁具体过程

在下图中加锁的时候会先检查锁的状态(state),这里state是一个很重要的概念,state代表这里的锁是否已经被别人获取了,0代表没有,>1则代表已经加锁,这里>1是因为一个线程可以获取方法中的锁多次(例如迭代的情况),state是几就是几次,这就是可重入锁的由来。
这里是一个cas操作。
在这里插入图片描述

具体实现如下图,调用的是native方法,本质是C++写的方法
在这里插入图片描述
在这里插入图片描述

上图中的参数中stateOffset是指state属性在内存中的位置,具体如下图所示
在这里插入图片描述

如果锁还没有被获取那么线程获取锁(独占锁),如果锁已经被获取了,那么就去排队(或插队),如下图所示
在这里插入图片描述

acquire方法如下,在去AQS排队之前,还是调用TryAcquire方法尝试去获取锁,这就是插队
在这里插入图片描述

TryAcquire方法在NonfairSync中被重写
在这里插入图片描述

实际上是调用
在这里插入图片描述

尝试获取锁的过程如下,如果这时候成功获取锁,那么就不需要排队了
在这里插入图片描述

当获取锁失败的时候执行acquireQueued方法,也就是排队获取锁
在这里插入图片描述

在执行acquireQueued方法之前会调用addWaiter方法把当前线程封装成Node类,Node类结构如下,其中waitStatus标明了Node的模式(独占或者共享)
在这里插入图片描述

AddWaiter方法如下
在这里插入图片描述

Enq方法如下,节点的链表结构完成之后,节点就已经排好队了
在这里插入图片描述

接下来就是用新建的Node去获取锁了,调用acquireQueued方法,具体过程如下
在这里插入图片描述

尝试去获取锁在前面已经介绍了,获取失败后会去调用shouldParkAfterFailedAcquire方法如下图
在这里插入图片描述

设置设置好节点之后调用parkAndCheckInterrupt方法挂起该节点,如下图
在这里插入图片描述

至于为什么要返回当前线程是否被中断,因为线程挂起的时候是无法响应中断的,必须等线程重新启用后才能处理中断,下图是处理中断的地方,在完成锁的获取之后根据中断状态决定当前线程是否继续执行下去。
在这里插入图片描述

以上是Node等待获取锁的过程,当Node没有获取到锁,并且又退出了自旋,那么会调用
cancelAcquire方法

在这里插入图片描述

其方法如下
在这里插入图片描述

在这里插入图片描述

节点唤醒方式如下
在这里插入图片描述
在这里插入图片描述

至于为什么从尾部节点开始遍历,原因如下
在这里插入图片描述

以上所有的步骤是获取锁,获取不到就被挂起,等待唤醒。当持有锁的线程unLock的时候,排队中的线程就会被唤醒,下面来看一下unLock方法
在这里插入图片描述

锁调用了release方法,如下
在这里插入图片描述

首先会tryRelease,这个方法在ReentrantLock的内部类sync中被重写,如下
在这里插入图片描述

非重入锁直接解锁,使用unparkSuccessor方法唤醒下一个节点,该方法上面已经解释过了
在这里插入图片描述

到此,加锁,排队,解锁的过程都已经结束。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值