ReentrantLock源码阅读
目录本人的源码阅读主要聚焦于类的使用场景,一般只在java层面进行分析,没有深入到一些native方法的实现。并且由于知识储备不完整,很可能出现疏漏甚至是谬误,欢迎指出共同学习
本文基于corretto-17.0.9源码,参考本文时请打开相应的源码对照,否则你会不知道我在说什么
简介
ReentrantLock是可重入独占锁,所谓可重入指的是占有锁的线程继续在这个锁上调用lock直接加锁成功,当然,lock与unlock的调用次数最终数量要相等,否则不会释放锁。而不可重入锁则是lock成功后再lock就会被阻塞。ReentrantLock基于AQS(AbstractQueuedSynchronizer),详情请看AbstractQueuedSynchronizer源码阅读
例子
ReentrantLock使用比较简单,随便看看就行,比如实现一个线程安全的计数器:
public class Counter {
private final Lock lock = new ReentrantLock();
private int count;
public void add(int n) {
lock.lock();
try {
count += n;
} finally {
lock.unlock();
}
}
}
代码分析
类似在AQS一文中实现的Mutex,ReentrantLock的方法基本也是通过AQS的方法实现的,因此直接看基于AQS的私有静态内部子类Sync如何实现的。另外需要明确是的,ReentrantLock是独占锁,因此可以忽略共享模式的相关方法,如tryAcquire
。
ReentrantLock支持公平锁和非公平锁。至于公平与否的概念,详情可见我AQS源码阅读的文章,我定义了三个公平级别,ReentrantLock支持的公平锁为强公平级别,支持的非公平锁为于弱公平级别。
ReentrantLock的内部静态类FairSync和NonfairSync分别用于支持公平和非公平锁,两者都从Sync类派生而来,因此先介绍Sync这个类的核心方法。
Sync.tryLock
final boolean tryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
tryLock功能是尝试加锁,返回加锁是否成功,成功的话重入次数+1,另外这个tryLock是非公平的,因为它没有检测等待队列是否有线程在等待,而是直接尝试修改state加锁。
读过AQS那篇的话,这个方法读起来可谓是毫无压力的,ReentrantLock其他方法读起来同样也很简单(即使没仔细研究过AQS读这个其实也不难)。要注意的是AQS.state的类型是int,一直lock而不unlock的话会造成int溢出,直接抛一个Error。
Sync.initialTryLock
abstract boolean initialTryLock();
initialTryLock的功能同样是尝试加锁,在lock中的开头调用一次。由于公平与否的区别在于尝试加锁前有没有检查等待队列,因此提取这个方法留给子类实现。后面会介绍实现。
Sync.lock
final void lock() {
if (!initialTryLock())
acquire(1);
}
lock的功能就是字面意思:加锁。实现很简单,尝试加锁失败就通过acquire获取锁。
Sync.tryRelease
protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (getExclusiveOwnerThread() != Thread.currentThread())
throw new IllegalMonitorStateException();
boolean free = (c == 0);
if (free)
setExclusiveOwnerThread(null);
setState(c);
return free;
}
tryRelease是重写AQS的方法,功能就是尝试释放锁。如果不是持有锁的线程尝试释放锁则直接抛异常,否则就将state(重入数)减去相应的次数。
上面几个就是Sync的核心方法。还记得AQS实现独占锁要实现的三个方法吗?tryAcquire
,tryRelease
和isHeldExclusively
(这个实现很简单,不展示了),其中tryAcquire同样因为公平与否的特性,交给子类实现。综上,子类需要实现tryAcquire
和initialTryLock
两个方法:
NonFairSync.initialTryLock
final boolean initialTryLock() {
Thread current = Thread.currentThread();
if (compareAndSetState(0, 1)) { // first attempt is unguarded
setExclusiveOwnerThread(current);
return true;
} else if (getExclusiveOwnerThread() == current) {
int c = getState() + 1;
if (c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
} else
return false;
}
嗯,看着有点眼熟呢,是的,跟Sync.tryLock几乎毛区别没有...不知道为什么不直接调用tryLock
NonFairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
也非常简单呢,不过注意lock和acquire的区别,lock可能是让state从0到1,从1到2...acquire就是纯纯的从0到1,也就是acquire的语义是本来线程是不占有锁的,现在要用acquire获取到锁,所以是从0到1。这里个函数很简单,但在这啰嗦了点,算是闲来无事突发的一些感悟。
FairSync.initialTryLock
final boolean initialTryLock() {
Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedThreads() && compareAndSetState(0, 1)) {
setExclusiveOwnerThread(current);
return true;
}
} else if (getExclusiveOwnerThread() == current) {
if (++c < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(c);
return true;
}
return false;
}
这个函数与NonFairSync.initialTryLock的区别只有:state从0到1之前先检查一下有没有正在等待队列的线程,以实现强公平。
FairSync.tryAcquire
protected final boolean tryAcquire(int acquires) {
if (getState() == 0 && !hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
这个函数与NonFairSync.tryAcquire的区别只有:state从0到1之前先检查有没有比当前线程等待更久的线程。因为tryAcquire可能会在好几个时间点调用(线程进入等待队列之前,线程进入等待队列之后),所以使用hasQueuedPredecessors来检查,这个函数在以下两种情况下返回true:
- 当前线程没进入阻塞队列,且队列不为空
- 当前线程已经在阻塞队列中,但前面有更靠近队头(等待时间更长)的线程
这两种情况下都得获取锁失败,以实现强公平。
至于ReentrantLock其他的方法实在没必要解析了,比如:
public void lock() { sync.lock(); }
public boolean tryLock() { return sync.tryLock(); }
public boolean hasWaiters(Condition condition) {
if (condition == null)
throw new NullPointerException();
if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
throw new IllegalArgumentException("not owner");
return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
是吧...
参考链接
「博客园」AbstractQueuedSynchronizer源码阅读
标签:return,int,lock,ReentrantLock,tryAcquire,源码,线程,阅读 From: https://www.cnblogs.com/nosae/p/17931415.html