码迷,mamicode.com
首页 > 其他好文 > 详细

ReentrantLock获取锁、释放锁源码浅析

时间:2019-06-17 01:15:02      阅读:175      评论:0      收藏:0      [点我收藏+]

标签:循环   nbsp   否则   wait   互斥锁   互斥   构造   image   抽象   

JUC包下的ReentrantLock是基于Aqs模板实现的,它区分公平锁和非公平锁,内部实现了两个同步器,本文关注非公平锁部分。

伪代码

我们先看两个伪代码:

1、获取锁

 1 if(获取锁成功) {
 2     return
 3 } else {
 4     加入等待队列
 5     for(死循环) {
 6         if (获取到锁) {
 7             return
 8         } 
 9         阻塞线程
10     }
11 }

我们看到,如果一次获取成功则结束,如果没有获取成功将进入循环中,并且当前线程阻塞直到被唤醒并且获取到锁才结束。

2、释放锁

1 if(释放锁) {
2     唤醒等待队列中阻塞的首个线程
3 }

释放锁的逻辑比较简单,如果当前锁释放了,唤醒下一个。

DEMO

通过伪代码了解了ReentrantLock基本原理以后,我们从一个DEMO入手,看看它的源码实现

技术图片

获取锁

DEMO中初始化了一个ReentrantLock实例,并且调用了lock()方法,在结束的时候调用了unlock()方法。我们先进入lock()方法看看互斥锁是怎么加锁的

技术图片

ReentrantLock内部调用了一个sync对象的lock()方法,我们看看sync是什么

技术图片

这里Sync是一个继承了Aqs模板的同步器抽象类,Sync有两个实现类,一个是非公平锁实现

技术图片

一个是公平锁实现

技术图片

好吧,简单来说就是ReentrantLock在初始化的时候构造了一个NonfairSync或者FairSync对象。并且在调用lock()方法的时候,其实是去调用这个Sync实例对象的lock()方法而实现的加锁操作。那么我们就看看Sync实例是怎么实现加锁的,进入NonfairSync的lock方法。

技术图片

lock()方法先取执行了一个CAS操作,把一个state变量从0设置为1,state变量是声明在Aqs模板的成员变量

技术图片

ReentrantLock在设计的时候认为state > 0的时候表示锁被持有,state = 0的时候表示锁没有持有者。所以这里的cas机制做了一次尝试获取锁的操作。如果获取成功了,那么就将当前线程设置为锁的持有者。如果设置失败了,意味着当前有持有者,那么调用Aqs的acquire方法去获取,我们看看Aqs的acquire方法做了什么

技术图片

acquire方法,先尝试tryAcquire获取一次锁,如果获取成功则结束,我们看看NonfairSync怎么实现tryAcquire的

技术图片

逻辑也很简单,采用CAS做了一次设置,如果是重入的话,那么state + 1。

如果失败先调用addWaiter方法,再调用acquireQueued方法。我们先看看addWaiter做了什么

技术图片

我们看到,Thread被包装成了一个Node节点,Aqs中是使用链表的方式来实现等待队列的,如

技术图片

总之,当前节点将会被添加到队列的尾巴,如果没有添加成功调用enq()方法,我们看看enq方法

技术图片

enq方法其实就是通过自旋操作保证添加到队列的尾巴,所以addWaiter的核心就是没有获取到锁的线程被加入到队列中。我们再回到acquire方法中看看acquireQueued在addWaiter做了什么

技术图片

acquireQueued方法的逻辑比较简单,其实就是在一个for循环中自旋,如果轮到当前节点了,那么就跳出循环。否则被阻塞,直接被唤醒重新自旋。

以上获取锁的源码,与一开始的伪代码逻辑是一样的,如果获取到锁那么state设置为1,重入再+1。如果获取锁失败,那么Thread被包装成Node加入到链表的最尾巴。并且在for循环里面判断是否轮到当前Node获取锁了。

释放锁

 下面再看看ReentrantLock的unlock方法

技术图片

跟lock一样,直接调用了Sync同步器的release方法

技术图片

释放的逻辑比较简单,先调用了一次tryRelease,如果成功的话将等待队列里面的下个线程唤醒就结束了。我们看看NonfairSync是怎么实现tryRelease的吧

技术图片

其实就是把state扣减,如果等于0的话,意味着完全释放锁,那么把独占锁的线程设置为null即可。

总结

从ReentrantLock中,我们看到它基于Aqs实现了Sync同步器,而同步器基本上只是实现了一个tryAcquire方法和tryRelease方法,他们分别会被Aqs中的acquire方法和release方法调用。其它的逻辑,比如入队出队全都被Aqs自己实现了。同时我们还看到了Aqs中多出使用CAS机制来控制数据的更改。另外Aqs提供一个state变量给我们,我们至于怎么使用它需要我们自己设计,比如ReentrantLock将state > 0设计为获取锁,state = 0设计为没有任何获取锁的线程。

 

ReentrantLock获取锁、释放锁源码浅析

标签:循环   nbsp   否则   wait   互斥锁   互斥   构造   image   抽象   

原文地址:https://www.cnblogs.com/lay2017/p/11037516.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!