ReentrantLock介绍
ReentrantLock也是在JUC并发包下的,实现可重入锁的机制。也是在AQS基础之上的,在上一篇AQS的讲解基础下,理解这篇会比较的简单。知识图谱整理之Java基础AbstractQueuedSynchronizer
。
ReentrantLock的概览
要了解这个类,主要还是从它的使用方法上入手,因为这个是的数据结构基础是在AQS之上的,所以内部不再重写数据结构,主要关注点还是在于这个类的方法。
在这个讲输出的年代,ReentrantLock是如何实现输出的呢?他其实是内部维护了一个继承AQS的类,然后在通过获取锁是否公平的方式分为了两种策略,即公平锁和非公平锁。首先我们来看下这个基础类Sync源码。
源码解读
Sync内部类源码
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
/**
* Performs {@link Lock#lock}. The main reason for subclassing
* is to allow fast path for nonfair version.
*/
abstract void lock();
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
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;
}
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() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
final ConditionObject newCondition() {
return new ConditionObject();
}
// Methods relayed from outer class
final Thread getOwner() {
return getState() == 0 ? null : getExclusiveOwnerThread();
}
final int getHoldCount() {
return isHeldExclusively() ? getState() : 0;
}
final boolean isLocked() {
return getState() != 0;
}
/**
* Reconstitutes the instance from a stream (that is, deserializes it).
*/
private void readObject(java.io.ObjectInputStream s)
throws java.io.IOException, ClassNotFoundException {
s.defaultReadObject();
setState(0); // reset to unlocked state
}
}
- 首先可以看到这个类是个抽象类,所以可以确定它肯定有子类实现。
- nonfairTryAcquire方法比较关键,是非公平尝试获取锁,里面的逻辑其实就比较简单了,无非是先获取state变量的值,通过CAS方式修改,如果修改成功就代表获取到了锁标识。如果失败,会判断当前线程是否为已获取到锁标识的线程,如果是则state+1来代表重入次数。
- tryRelease的逻辑就不多说了,就是关于state变量操作
- 这个类主要是这两个方法比较重要,其他的等我们用到了在做深入
NonfairSync内部类源码
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
- 这里就实现了Sync的lock方法,也比较的简单
- 如果获取锁不成功,我们看到调用了acquire,这是AQS的方法,还记得这里的操作么?不记得可以看看AQS的,大概就是调用了tryAcquire方法,如果不行就放进同步等待队列。
- tryAcquire就是调用了Sync的nonfairTryAcquire方法,这样整体就串起来了
FairSync内部类源码
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
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;
}
}
- 首先这个是公平锁,何为公平锁,就是先进来的先获取到锁。基于这个条件我们来看下是如何实现的
- 首先是lock方法,很简单就是调用了AQS的acquire,在调用FairSync实现的tryAcquire方法
- tryAcquire是公平锁的关键,就是在state为0时,即可以争取锁时,哪些线程可以争。
- hasQueuedPredecessors方法很关键,这个方法作用是查询是否有任何线程在等待获取比当前线程更长的。
hasQueuedPredecessors方法源码
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// thread is first in queue.
Node t = tail; // Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
- 第一个条件 h != t代表如果当前没有任何其他竞争线程在队列中的,直接返回false。h==t代表没有其他在等待的竞争线程
- ((s = h.next) == null || s.thread != Thread.currentThread())这个条件判断是否头结点的下一个节点的线程是否是当前线程。这里要结合这个方法的上层,是判断如果非则继续执行,所以整体逻辑就是如果是当前线程,则继续往下执行。
锁如何对外输出
从构造方法开始:
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
public void lock() {
sync.lock();
}
其实就是用了策略模式,确定锁策略,实现交给对应策略子类。
今日总结
今天阅读了关于ReentrantLock的源码,在理解了AQS的基础上,阅读起来比较简单,再来晒下今天的知识图谱输出: