1、前言
在前面我们已经了解到Lock接口,是对锁操作方法的一个基本定义,它提供了synchronized关键字所具备的全部功能方法,而ReentrantLock类不仅完全实现了显示锁Lock接口所定义的接口,也扩展了对使用显式锁Lock的一些监控方法。同时,我们也尝试使用ReentrantLock实现了对共享资源的同步访问,这一节我们来深入了解,这些功能是如何实现的。
2、ReentrantLock(重入锁)实现原理
2.1、ReentrantLock对接口方法的实现
通过查看ReentrantLock类的源码,我们会发现所有实现Lock接口接口的方法,包括额外增加的一些监控方式,它们其实都是借助一个Sync变量实现的,如下所示:
public void lock() {
sync.lock();
}
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
public void unlock() {
sync.release(1);
}
public Condition newCondition() {
return sync.newCondition();
}
//ReentrantLock实现的一些监控方法,同样也是借助sync实现的,代码如下(篇幅原因,仅显示了其中一个说明情况)
public int getHoldCount() {
return sync.getHoldCount();
}
//……
通过上述的部分源码,我们已经可以确定ReentrantLock类的实现,主要基于sync对象,只要我们把Sync对象研究清楚了,那么ReentrantLock类的原理我们自然而然的就搞清楚了,下面我们开始学习Sync类的实现。
2.2、ReentrantLock构造函数
ReentrantLock提供了公平锁和非公平锁的机制,主要基于Sync 的实现类FairSync和NonfairSync实现。默认构造函数使用的是非公平锁的机制,可以使用带参数的构造函数,应用公平锁机制。构造函数如下:
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public ReentrantLock() {
sync = new NonfairSync();
}
2.2、Sync内部类
ReentrantLock对Lock的实现都是基于Sync来做的,在前面的源码已经清楚的看到了,这里我们就重点来学习Sync类的实现。
Sync类的层级结构:
根据Sync的层级结构,我们可以知道该类继承了AbstractQueuedSynchronizer(AQS)抽象类,同时有两个实现类FairSync和NonfairSync,分别应用于公平锁和非公平锁机制。其中,AbstractQueuedSynchronizer抽象类定义了一套多线程访问共享资源的同步模板,解决了实现同步器时涉及的大量细节问题,能够极大地减少实现工作,即AbstractQueuedSynchronizer为加锁和解锁过程提供了统一的模板函数,Sync类只需要实现部分抽象方法即可。
这里暂时不深入学习AbstractQueuedSynchronizer(AQS)抽象类,后续专门一篇进行介绍。
在Sync类中,主要实现了nonfairTryAcquire()和tryRelease()方法,当然,还有一些其他方法的实现, 具体请看源码的注释:
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* 抽象方法,在实现类FairSync和NonfairSync中实现,
* 在实现类的方法中,决定了获取锁的公平锁或非公平锁的策略
*/
abstract void lock();
/**
* 非公平锁-尝试获取锁资源的方法,而公平锁尝试获取资源的方法却在实现类FairSync中实现。
*/
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取state状态,表示锁状态,getState()方法在AbstractQueuedSynchronizer(AQS)抽象类中实现。
int c = getState();
if (c == 0) { //等于0,表示资源可用
//cas方法设置状态,保证原子性
if (compareAndSetState(0, acquires)) {
//获取成功,设置持有锁的线程,并返回获取锁成功
setExclusiveOwnerThread(current);
return true;
}
}else if (current == getExclusiveOwnerThread()) {//判断当前线程是否持有锁,可重入特性
//设置state的数值
int nextc = c + acquires;
if (nextc < 0) // 超过上限时
throw new Error("Maximum lock count exceeded");
//当前线程设置,不需要cas方式
setState(nextc);
return true;
}
return false;
}
/**
* 释放锁资源
*/
protected final boolean tryRelease(int releases) {
//计算如果释放成功后,当前线程持有的hold数量
int c = getState() - releases;
//判断是否是当前线程释放锁,不是的话,抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//如果c==0,说明释放锁成功,同时把持有锁的线程设置为null
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//c==0时,free=true,释放锁成功,c!=0时,free=false,释放锁失败,说明有重入操作
setState(c);
return free;
}
/**
* 当前线程是否持有锁
*/
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
/**
* 创建Condition对象
*/
final ConditionObject newCondition() {
return new ConditionObject();
}
/**
* 获取持有锁的线程
*/
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
/**
* 获取Hold数量,是当前线程持有锁,就返回state,否则(当前线程没有持有锁时),返回0
*/
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
/**
* 锁资源是否被占用
*/
final boolean isLocked() {
return getState() != 0;
}
/**
* 反序列化
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
2.3、NonfairSync实现类 (非公平锁策略)
获取锁失败的线程,会进入CLH队列阻塞,其他线程解锁会唤醒CLH队列线程,当重新竞争锁时,如果有非CLH队列的线程(还没来得及进入CLH队列阻塞的线程)参与竞争,那么就是非公平锁策略。
非公平锁策略实现类NonfairSync ,主要实现了Sync类的抽象方法lock(),在该方法中,实现了非公平锁策略;同时还实现了tryAcquire()方法(主要逻辑在Sync类的nonfairTryAcquire()方法中实现),用于获取,具体如下:
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* 获取锁资源,个人感觉如果发生锁竞争时,正好有线程来获取锁,是不是会比CLH阻塞队列中的线程更容易获得锁呢?
*/
final void lock() {
if (compareAndSetState(0, 1))//cas方法尝试设置,如果设置成功,当前线程就可以获取锁
setExclusiveOwnerThread(Thread.currentThread());
else
//在AbstractQueuedSynchronizer(AQS)抽象类中实现,执行AQS获取锁的流程
acquire(1);
}
/**
* 尝试获取锁资源,在Syns类中定义nonfairTryAcquire()方法
*/
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
2.4、FairSync实现类 (公平锁策略)
FairSync流程与NonfairSync类似,都是实现了lock()和tryAcquire()方法,区别在于:
- lock() 方法实现中,在FairSync中直接调用了acquire(1)方法,而在NonfairSync类中,首先进行了cas操作,然后失败后才调用了acquire(1)方法。
- 在tryAcquire()方法中,FairSync类在自己内部实现逻辑,且在在cas操作前,多了一步hasQueuedPredecessors函数,验证是否在CLH阻塞队列,而NonfairSync类中,则是调用了nonfairTryAcquire()方法,且不需要验证是否在CLH阻塞队列。
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
//直接调用AbstractQueuedSynchronizer(AQS)抽象类中acquire(),执行AQS获取锁的流程
acquire(1);
}
/**
* 获取锁资源
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
//hasQueuedPredecessors()方法在AQS抽象类中定义,验证当前现场是否在CLH阻塞队列中,不在队列中,则获取锁失败,同时还需要cas操作验证。
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
3、总结
至此,关于ReentrantLock的实现原理我们就学习完了,但是其中涉及到的CLH阻塞队列、AQS抽象类中的hasQueuedPredecessors()、acquire()等方法,我们都没有深入学习,我们计划在下一节中,专门深入研究一下AQS的实现原理,敬请期待!!!
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/68705.html