先了解一下
读本篇前,一定要确保已经读过本公众号的AQS讲解。
我们知道实现一把锁要有如下几个逻辑
- 锁的标识
- 线程抢锁的逻辑
- 线程挂起的逻辑
- 线程存储逻辑
- 线程释放锁的逻辑
- 线程唤醒的逻辑
我们在讲解AQS的时候说过AQS基本负责了实现锁的全部逻辑,唯独线程抢锁和线程释放锁的逻辑是交给子类来实现了,而ReentrantLock作为最常用的独占锁,其内部就是包含了AQS的子类实现了线程抢锁和释放锁的逻辑。
我们在使用ReentrantLock的时候一般只会使用如下方法
ReentrantLock lock=new ReentrantLock();
lock.lock();
lock.unlock();
lock.tryLock();
Condition condition=lock.newCondition();
condition.await();
condition.signal();
condition.signalAll();
技术架构
如果我们自己来实现一个锁,那么如何设计呢?
根据AQS的逻辑,我们写一个子类sync,这个类一定会调用父类的acquire方法进行上锁,同时重写tryAcquire方法实现自己抢锁逻辑,也一定会调用release方法进行解锁,同时重写tryRelease方法实现释放锁逻辑。
图片
那么ReentrantLock是怎么实现的呢?
ReentrantLock的实现的类架构如下,ReentrantLock对外提供作为一把锁应该具备的api,比如lock加锁,unlock解锁等等,而它内部真正的实现是通过静态内部类sync实现,sync是AQS的子类,是真正的锁,因为这把锁需要支持公平和非公平的特性,所以sync又有两个子类FairSync和NonfairSync分别实现公平锁和非公平锁。
图片
因为是否公平说的是抢锁的时候是否公平,那两个子类就要在上锁方法acquire的调用和抢锁方法tryAcquire的重写上做文章。
公平锁做了什么文章?
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
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;
}
}
公平锁比较简单,直接调用了父级类AQS的acquire方法,因为AQS的锁默认就是公平的排队策略。
重写tryAcquire方法的逻辑为:
公平就公平在老老实实排队。
非公平锁做了什么文章?
static final class NonfairSync extends Sync {
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//nonfairTryAcquire代码在父类sync里面
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;
}
非公平锁也很简单,没有直接调用了父级类AQS的acquire方法,而是先通过cas抢锁,它不管等待队列中有没有其他线程在排队,直接抢锁,这就体现了不公平。
它重写tryAcquire方法的逻辑为:
公平锁和非公平分别重写了tryAcquire方法,来满足公平和非公平的特性。那么tryAcquire方法也是需要子类重写的,因为它和是否公平无关,因此tryAcquire方法被抽象到sync类中重写。
sync类中
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;
}
释放锁的逻辑如下:
释放锁往往和抢锁逻辑是对应的,每个子类抢锁逻辑不同的话,释放锁的逻辑也会对应不同。
具体实现
接下来我们通过ReentrantLock的使用看下它的源码实现
class X {
private final ReentrantLock lock = new ReentrantLock();
Condition condition1=lock.newCondition();
Condition condition2=lock.newCondition();
public void m() {
lock.lock();
try {
if(条件1){
condition1.await();
}
if(条件2){
condition2.await();
}
} catch (InterruptedException e) {
} finally {
condition1.signal();
condition2.signal();
lock.unlock();
}
}
}
先看这个方法:lock.lock()
ReentrantLock类
public void lock() {
sync.lock();
}
NonfairSync 类中
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
FairSync 类中
final void lock() {
acquire(1);
}
公平锁和非公平锁中都实现了lock方法,公平锁直接调用AQS的acquire,而非公平锁先抢锁,抢不到锁再调用AQS的acquire方法进行上锁
进入acquire方法后的逻辑我们就都知道了。
再看这个方法lock.unlock()
public void unlock() {
sync.release(1);
}
unlock方法内直接调用了AQS的Release方法进行解锁的逻辑,进入release方法后逻辑我们都已经知道了,这里不再往下跟。
最后看这个方法lock.tryLock()
public boolean tryLock(long timeout, TimeUnit unit)
throws InterruptedException {
return sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
tryLock方法直接调用sync的tryAcquireNanos方法,看过AQS的应该知道tryAcquireNanos这个方法是父类AQS的方法,这个方法和AQS中的四个核心方法中的Acquire方法一样都是上锁的方法,无非是上锁的那几个步骤,调用tryAcquire方法尝试抢锁,抢不到锁就会进入doAcquireNanos方法。
public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}
doAcquireNanos这个方法做的其实就是入队,阻塞等一系列上锁操作,逻辑和Acquire方法中差不多,但是有两点不同:
看下下面的代码
private boolean doAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (nanosTimeout