ReentrantLock获取锁释放锁源码浅析
Posted lay2017
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ReentrantLock获取锁释放锁源码浅析相关的知识,希望对你有一定的参考价值。
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获取锁释放锁源码浅析的主要内容,如果未能解决你的问题,请参考以下文章