本文源码基于: JDK13
前言
上一篇文章讲了AQS的基本原理,其中两个关键的操作: 获取/释放.
依赖于子类的实现,本文就借着学习ReentrantLock的同时,继续巩固一下AQS.
ReentrantLock支持公平锁以及非公平锁,实现源于内部不同的AQS子类同步器.
公平锁: 加锁时候按照线程申请顺序. 公平一点.
非公平锁: 不保证按照顺序. 性能好一点.
Sync同步器
既然ReentrantLock是基于AQS实现的,那么肯定是继承了AQS来实现了一个同步器,首先就来看Sync的代码。
Sync继承自AQS,主要实现了两个方法:
nonfairTryAcquire(int acquires)
为非公平锁开发的一个获取锁方法:
1 | /** |
首先拿到当前的state值.
如果state值为0.说明锁当前空闲,那么通过cas进行更改state的值.同时将锁的占用线程改成当前线程.
如果state不等于0. 且当前线程是锁的占用线程,那就将state值加上此次申请的值. 之后设置state值. 这个步骤也就是ReentrantLock是可重入锁的关键,
当发现锁的占用线程,就是当前线程时,不是加锁失败,而是叠加的加锁. 当然释放时也需要释放对应多的次数.
不满足以上两个条件,加锁失败,返回false.
tryRelease(int release)
解锁操作就不用区分公平还是非公平锁了.
1 |
|
.
如果当前线程不是持有锁的线程,抛出异常.
如果获取当年的state.减去此次要释放的后. 为0. 说明成功的释放锁了, 将锁的state置为0. 持有线程置为空。
如果不为0.设置新的状态,不修改持有线程,同时返回false.因为还没有完全释放锁.
FairSync
公平锁,实现了公平锁的获取操作.
1 | /** |
- 获取当前state
- 如果state=0,说明锁空闲, 如果等待队列中也没有节点,同时获取锁成功,就将当前线程设置为锁的持有节点. 返回true.
- 如果锁不空闲,但是当前线程就是锁的持有线程,对state进行累加操作.
- 如果以上都不符合,加锁失败. 返回false.
NonFairSync
非公平锁,他的获取操作,调用Sync类中的nonfairTryAcquire
.
ReentrantLock构造器
1 | /** |
两个构造器,默认是非公平锁,可以指定创建一个公平锁.
lock 加锁
加锁操作,调用链如下:
完全等同于AQS的acquire
操作,只是在tryAcquire
调用了ReentrantLock自己实现的方法.
tryLock()
调用同步器NonFairSync
的nonfairTryAcquire
. 做一次获取的尝试,成功就返回true.否则返回false.
tryLock(long timeout, TimeUnit unit)
带有自动超时的tryLock.
unlock()
完全等同于AQS的release
操作,只是在tryRelease
调用了ReentrantLock自己实现的方法.
其他方法都是非核心方法,提供一些对于属性的读取,不再赘述.
总结
ReentrantLock基本上完全基于AQS的独占式加锁/解锁.
走的流程也是AQS的加锁/解锁流程,
只是在最核心的操作状态(State)上,依赖于ReentrantLock的实现而已.
ReentrantLock定义了State状态的具体值,+1/-1分别代表什么操作,
也因为对State可以执行累加操作,而获得了可重入特性.
完.
完。
联系我
最后,欢迎关注我的个人公众号【 呼延十 】,会不定期更新很多后端工程师的学习笔记。
也欢迎直接公众号私信或者邮箱联系我,一定知无不言,言无不尽。
以上皆为个人所思所得,如有错误欢迎评论区指正。
欢迎转载,烦请署名并保留原文链接。
更多学习笔记见个人博客或关注微信公众号 <呼延十 >——>呼延十