JUC代码浅析[2]——基于AQS的锁ReentrantLock
ReentrantLock是使用比较普遍的一个可重入锁,它是互斥的,一个锁只能被一个线程占有。它的方法基本都是委托给继承AQS的Sync实现的。其中Sync有两种实现,公平和非公平。Sync使用state(通过AQS暴露的getState和setState方法)保存线程的获取次数。总的策略为state次数为0时可以获得锁。大于0时,如果当前线程已经拥有该锁则可再次获得,否则进入AQS的调度逻辑(<JUC代码浅析——同步器AQS>中介绍)。下面为非公平ReentrantLock的NonfairSync实现,
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
如果state次数为0的话,获得成功并设置当前线程为锁的拥有者。
否则进入AQS调度,acquire(1)方法使用tryAcquire尝试获得,这里重载的tryAcquire调用的是nonfairTryAcquire方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果当前重进入数为0,有机会取得锁
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;
}
//都不满足,则进入AQS队列等待调度
return false;
}
公平ReentrantLock的FairSync实现
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//如果当前重进入数为0,有机会取得锁
if (c == 0) {
//如果当前线程是第一个等待者,获得成功并设置当前线程为锁的拥有者
if (isFirst(current) &&
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;
}
//都不满足,则进入AQS队列等待调度
return false;
}
fairSync和NonfairSync的tryRelease方法是同一个,比较简单,就是改变一下state次数,如果需要的话把拥有者线程置为null
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;
}
公平和非公平的区别就在于,尝试进入的时候需要判断当前线程是否是队列的第一个,是第一个才能进入。这样AQS在调度的时候就会阻塞掉很多线程,性能差别就比较大。ReentrantLock只支持互斥模式,没有共享模式