我的jdk源码(二十三):ReentrantLock类
Posted 1994july
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了我的jdk源码(二十三):ReentrantLock类相关的知识,希望对你有一定的参考价值。
一、概述
ReentrantLock类是在内部利用自己的内部类Sync继承了AbstractQueuedSynchronizer,实现了tryAcquire()方法,在这个方法中增强自己的功能,比如添加了重入和公平锁、非公平锁。ReentrantLock再将Sync作进一步的封装,开放出去Lock的接口。
ReentrantLock是通过在请求锁的时候判断CLH列表有没有比当前线程等待时间更久的线程来实现公平性的。当一个线程请求公平锁的时候,如果state为0,还需要在CHL中没有等待节点,或CHL首位就是当前线程,那么就可以使用CAS请求锁。否则请求直接失败。非公平锁没有这个限制,只要state为0,就可以使用CAS操作请求锁。非公平锁的吞吐量要比公平锁要高,因为公平锁就是完全要入队然后依次执行,在入队后线程会被park,到当前线程后需要unpark。线程之前的切换,资源准备等都是比较耗时的。非公平锁在在任务请求获取锁的时候如果正好锁被释放,则可以不用入队省去了线程阻塞唤醒。
二、源码分析
1. 类的声明
public class ReentrantLock implements Lock, java.io.Serializable
ReentrantLock类实现了Lock和Serializable接口,表示其实例是可序列化的琐。
2. 构造函数
// 构造一个 ReentrantLock 实例(非公平锁)
public ReentrantLock() {
sync = new NonfairSync();
}
// 构造一个 ReentrantLock 实例(指定是否公平)
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
3. 成员变量
//序列化标识id
private static final long serialVersionUID = 7373984872572414699L;
//同步器实例,可以是公平琐,也可以是非公平琐
private final Sync sync;
4. 内部类-Sync类
//Sync也是一个抽象类,因为锁有非公平和公平的区别。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
//非公平和公平锁的lock()方法有不同的实现。
abstract void lock();
//tryLock()在子类中实现,该方法是非公平的独占式获取同步状态。
//该方法首先判断同步状态是否被获取,如果没有,CAS获取同步状态并将锁的拥有者设为当前线程,如果有,判断获取锁的线程是否是当前线程,如果是,将同步值进行累加。
//成功获取锁的线程,再次获取锁,只是增加了同步值。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
//获取了多少次锁,同样也要释放多少次锁。
//当同步值不为0时,还是当前线程占有锁。
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState(c);
return free;
}
//是否被当前线程所占有
protected final boolean isHeldExclusively() {
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
//得到锁的占有者
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
//获取锁的次数
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
}
}
ReentrantLock通过将继承AQS的子类sync作为类成员变量来实现锁,sync实现AQS的抽象方法来管理同步状态。锁的获取和释放是通过修改 AQS 的 state 变量来实现的。lock 方法可以看做对 state 执行“加法”操作,而 unlock 可以看做对 state 执行“减法”操作,当 state 为 0 时,表示当前没有线程占用资源。
5. 内部类-NonfairSync类
//非公平锁的实现
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
//非公平锁加锁,acquire调用tryAcquire,tryAcquire调用nonfairTryAcquire
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
非公平锁的lock()方法步骤为:先尝试以 CAS 方式修改 state 的值,若修改成功,则表示成功获取到锁,将 owner 设为当前线程;否则就执行 AQS 中的 acquire 方法。
6. 内部类-FairSync类
//公平锁的实现
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
//lock()调用acquire(),acquire()调用tryAcquire(),公平锁和非公平锁的tryAcquire()的实现唯一不同
//就是加入了hasQueuesPredecessors(),通过判断当前节点是否有前驱节点,如果有则当前节点不是等待时间最长的线程
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
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;
}
}
非公平锁相比,公平锁的不同之处在于增加了判断条件 hasQueuedPredecessors,即首先判断主队列中是否有其他线程在等待,当没有其他线程在排队时再去获取,否则获取失败。
公平锁和非公平锁的主要在于两个地方:
(1) 获取锁lock()方法的不同:非公平锁会直接进行一次CAS,如果CAS成功,就直接获取锁了。
(2) tryAcquire()方法实现的不同:当同步状态此时被释放的时候,state等于0,并没有任何线程占有锁,如果是公平锁,会判断队列中是否有线程等待获取锁,如果有的话,直接将自己构造成一个节点加入同步队列,但是非公平锁会直接CAS设置状态,不考虑同步队列中是否有其他线程等待获取锁。如果获取同步状态失败,会将自己构造成一个节点加入同步队列中,加入同步队列之后,等待前驱节点释放同步状态,唤醒自己,这后面的步骤公平锁和非公平锁是一样的。
7. ReentrantLock其他方法
//构造函数默认是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
//可选择实现公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
//获取锁
public void lock() {
sync.lock();
}
//可中断式的获取锁
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
//尝试非阻塞的获取锁,调用方法之后立马返回,能获取返回true,不能获取返回false
public boolean tryLock() {
return sync.nonfairTryAcquire(1);
}
//释放锁
public void unlock() {
sync.release(1);
}
//获取等待通知组件,该组件和当前锁绑定
public Condition newCondition() {
return sync.newCondition();
}
//锁被获取的次数
public int getHoldCount() {
return sync.getHoldCount();
}
//锁是否被当前线程所占有
public boolean isHeldByCurrentThread() {
return sync.isHeldExclusively();
}
//锁是否已经被获取
public boolean isLocked() {
return sync.isLocked();
}
//是否是公平锁
public final boolean isFair() {
return sync instanceof FairSync;
}
//获取锁的所有者
protected Thread getOwner() {
return sync.getOwner();
}
//是否有线程等待该锁
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
//查询给定线程是否在等待该锁
public final boolean hasQueuedThread(Thread thread) {
return sync.isQueued(thread);
}
//查询等待锁的线程数
public final int getQueueLength() {
return sync.getQueueLength();
}
//返回等待锁的线程集合
protected Collection<Thread> getQueuedThreads() {
return sync.getQueuedThreads();
}
三、总结
ReentrantLock类总的来说,就是利用内部类sync类的子类NonfairSync类和FairSync类来实现非公平琐与公平琐,sync类则是继承自AbstractQueuedSynchronizer类。
ReentrantLock类有以下几点特点:
1. 是一个可重入锁,能保证同一个线程多次加锁。
2. 可以实现公平琐和非公平琐,初始化的时候可以指定类型。
3. 默认是非公平琐,线程每次再加入同步队列之前,都会尝试获取琐。
4. ReentrantLock类实现的琐可以看作是synchronized的升级版,可以手动获取琐和释放锁,并且可以绑定多个条件(基于AQS的Condition)。
更多精彩内容,敬请扫描下方二维码,关注我的微信公众号【Java觉浅】,获取第一时间更新哦!
推荐:http://www.1994july.club/?cat=518&paged=2
以上是关于我的jdk源码(二十三):ReentrantLock类的主要内容,如果未能解决你的问题,请参考以下文章
Spring源码分析(二十三)BeanFactory的后处理
android源码解析(二十三)-->Android异常处理流程
头秃系列,二十三张图带你从源码分析Spring Boot 启动流程~