ReentrantLock源码分析
这次来阅读一下ReentrantLock的源码,简单阅读,只看几个重要的方法。
首先ReentrantLock实现了Lock接口,Lock接口没啥好说的,提供了几个必须实现的方法。
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long var1, TimeUnit var3) throws InterruptedException;
void unlock();
Condition newCondition();
}
lock接口给出了必须实现的加锁和解锁的方法,来看看ReentrantLock是如何实现的。
首先,ReentrantLock可以通过构造方法传bool值来创建公平锁或非公平锁。
private final ReentrantLock.Sync sync;
public ReentrantLock() {
this.sync = new ReentrantLock.NonfairSync();
}
public ReentrantLock(boolean fair) {
this.sync = (ReentrantLock.Sync)(fair ? new ReentrantLock.FairSync() : new ReentrantLock.NonfairSync());
}
可以看到,ReentrantLock有一个内部类Sync
的引用,加锁和解锁的过程其实是由sync指向的对象来完成,无参构造函数将sync指向非公平锁的对象,带参数构造传入true则是公平锁对象,false则是非公平锁。因此,ReentrantLock默认是非公平锁。
再看其对Lock接口方法的实现。
public void lock() {
this.sync.acquire(1);
}
public void lockInterruptibly() throws InterruptedException {
this.sync.acquireInterruptibly(1);
}
public boolean tryLock() {
return this.sync.nonfairTryAcquire(1);
}
public boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException {
return this.sync.tryAcquireNanos(1, unit.toNanos(timeout));
}
public void unlock() {
this.sync.release(1);
}
public Condition newCondition() {
return this.sync.newCondition();
}
可以看到,每个方法的实现都是调用this.sync
这个内部类实现的。先看一下Sync这个内部类的结构。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
Sync() {
}
@ReservedStackAccess
final boolean nonfairTryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = this.getState();
if (c == 0) {
if (this.compareAndSetState(0, acquires)) {
this.setExclusiveOwnerThread(current);
return true;
}
} else if (current == this.getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
this.setState(nextc);
return true;
}
return false;
}
@ReservedStackAccess
protected final boolean tryRelease(int releases) {
int c = this.getState() - releases;
if (Thread.currentThread() != this.getExclusiveOwnerThread()) {
throw new IllegalMonitorStateException();
} else {
boolean free = false;
if (c == 0) {
free = true;
this.setExclusiveOwnerThread((Thread)null);
}
this.setState(c);
return free;
}
}
}
可以看到,Sync继承了AbstractQueuedSynchronizer
这个类,也就是神秘的AQS(抽象队列同步器),AQS类似与一个模板,封装了Lock接口下各种锁实现的公共方法。先作了解,下一章节再看看AQS是个什么东西。这里只列举了两个重要的方法,nonfairTryAcquire()
和tryRelease()
。分别对应与Lock接口的tryLock()
和tryUnLock()
。
非公平锁
先看看nonfairTryAcquire方法
int c = this.getState() //获取锁重入的次数
if(c == 0) //如果重入次数为0,也就是锁没有被任何线程占有
this.compareAndSetState(0, acquires) //使用CAS修改重入次数,CAS(0-->1)
this.setExclusiveOwnerThread(current) //CAS修改成功后,修改锁标记为当前线程并返回true
if(c != 0) //如果重入次数不为0,即已有线程占有锁
if (current == this.getExclusiveOwnerThread()) //如果占有锁的线程是当前线程,就重入锁
int nextc = c + acquires; //重入后,重入次数将修改为nextc
if (nextc < 0) { //如果c超出Int.MaxValue,nextc可能为负数,此时应报错
throw new Error("Maximum lock count exceeded");
}
this.setState(nextc); //修改state重入次数,返回true
这就是ReentrantLock的tryLock()方法源码的实现。其实Lock加锁就是不断的tryLock尝试抢锁的过程。抢锁的过程中有两个属性是重点,重入次数state和锁被占有的线程exclusiveOwnerThread。简单来说就是:
- 判断重入次数state是否为0
- state为0,说明没有线程占有锁,通过CAS操作修改state
- 修改state成功后,即抢锁成功,马上修改exclusiveOwnerThread为当前线程,返回true
- 如果state不为0,说明已有线程占有锁,判断这个线程是否是当前线程,若不是则加锁失败,返回false
- 若这个线程是当前线程,则重入锁,即修改state重入次数加1,返回true
再看看tryRelease方法
int c = this.getState() - releases; //解锁后重入的次数
if (Thread.currentThread() != this.getExclusiveOwnerThread()) //如果占有锁的线程不是当前线程,则报错
//否则
if(c == 0) //锁释放完毕
this.setExclusiveOwnerThread((Thread)null); //修改锁标记为null,修改state,返回true
if(c != 0) //锁未释放完毕
//修改state,返回false
这就是ReentrantLock的unLock()方法源码的实现。尝试解锁的过程为:
- 看锁是否是被当前线程占有,若不是当前线程占有,则会报
IllegalMonitorStateException
这个错误 - 若是当前线程占有,则通过c = state - release是否为0判断锁是否释放完毕
- 若c为0,锁释放完毕,修改exclusiveOwnerThread为null,返回true
- 若c不为0,说明锁未释放完毕,修改state,返回false
然而ReentrantLock中lock()方法 调用的是acquire()方法,在ReentrantLock中没看到此方法,因为这个方法是AQS实现的,先看看源码:
public final void acquire(int arg) {
if (!this.tryAcquire(arg) && this.acquireQueued(this.addWaiter(AbstractQueuedSynchronizer.Node.EXCLUSIVE), arg)) {
selfInterrupt();
}
}
static void selfInterrupt() {
Thread.currentThread().interrupt();
}
意思就是
- 尝试抢锁
- 若抢锁成功,!tryAcquire()为false,不做任何操作
- 若抢锁失败,!tryAcquire()为true,再判断当前线程在等待队列中是否是队列头部,若是队列头部则继续抢锁,若不是队列头部,则将此线程加入等待队列,并将线程挂起。
公平锁
最后再来看看ReentrantLock的公平锁实现方式,公平锁通过在构造方法中传入true来使sync引用指向公平锁的对象,公平锁这个内部类继承自Sync,重写了其tryAcquire()方法。看看源码。
static final class FairSync extends ReentrantLock.Sync {
private static final long serialVersionUID = -3000897897090466540L;
FairSync() {
}
@ReservedStackAccess
protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = this.getState();
if (c == 0) {
//加入对队列头部的判断
if (!this.hasQueuedPredecessors() && this.compareAndSetState(0, acquires)) {
this.setExclusiveOwnerThread(current);
return true;
}
} else if (current == this.getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) {
throw new Error("Maximum lock count exceeded");
}
this.setState(nextc);
return true;
}
return false;
}
}
这个tryAcquire()方法与非公平锁中的方法基本一致,区别在于,不在等待队列中的线程无法直接抢锁成功,需先进入队列中等待其成为队列头部才可以抢到锁。其他方法与非公平锁一致。