ReentrantLock继承了Lock接口,实现了lock、lockInterrupibly、unlock、tryLock等方法。
构造方法:两个构造方法,无参构造方法默认是非公平锁,有参构造函数如果为true初始化变量为公平锁,false为非公平锁。
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
sync是同步器Sync类型,final类型
private final Sync sync;
下面是Sync类,继承了AbstractQueuedSynchronizer类,以下简称AQS,具备了同步器的功能。
abstract static class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = -5179523762034025860L;
// 提供抽象的lock方法,提供子类去重写
abstract void lock();
// 非公平锁实现获取锁的逻辑
final boolean nonfairTryAcquire(int acquires) {
// 获取当前线程
final Thread current = Thread.currentThread();
// 获取当前状态,getState()方法的目的是获取state变量当前的值,这个变量是AQS中的由volatile修饰的成员变量,
// 这个state状态的作用是记录锁被当前线程重入的次数,因为ReentrantLock的可重入的特性,如果需要知道当前锁
// 是否可以被其他锁占用,这时就需要使用一个变量state来记录获取一次锁+1,释放一次锁减1
int c = getState();
// 如果state为0,那么说明当前没有线程正在持有锁
if (c == 0) {
// 通过cas来给state赋值,表示获得锁的次数
if (compareAndSetState(0, acquires)) {
// 将当前线程设置为独占锁,这个方法是AQS的父类AbstractOwnableSynchronizer的方法
setExclusiveOwnerThread(current);
return true;
}
}
// 如果当前state不为0,说明当前已经有线程已经占用了独占锁
// 当前线程必须和已经设置了的独占锁的线程一致
else if (current == getExclusiveOwnerThread()) {
// 将当前state和要加的数量相加
int nextc = c + acquires;
// 因为是int类型,上限是2147483647,反向溢出就是-2147483648,溢出则抛出errror
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
// 将计算出来的state值赋
setState(nextc);
return true;
}
return false;
}
// 释放锁
protected final boolean tryRelease(int releases) {
// 将当前state减去要释放的锁的数量
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;
}
......
}
接下来看一下Sync下面的两个子类:NonfairSync、FairSync
//非公平锁
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
// 第一次获取锁方法,cas设置state为1,设置独占锁线程
final void lock() {
// 与公平锁不同的是,这里会自己先尝试占有锁,而公平锁会按照FIFO队列一个接一个的尝试
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
// 调用AQS的获取锁方法
acquire(1);
}
// 调用Sync的nonfairTryAcquire方法
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
//公平锁
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
// 非公平锁获取锁逻辑
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 如果当前没有线程持有锁
if (c == 0) {
// 如果当前队列里面没有正在等待的线程,并且设置重入次数state成功,就设置独占锁的线程
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// state大于0,重入次数增加
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
return false;
}
}
ReentrantLock总结:
ReentrantLock是可以替代synchronized关键字的AQS独占锁的实现,由于synchronized在线程竞争激烈情况下的锁升级且不可降级问题,ReentrantLock在线程竞争激烈的情况表现优秀,可以实现同一个线程多次重入获得锁,可以让使用者知道自己有没有获取到锁,在获取锁的时候可以响应中断,可以添加获取锁超时时间。
ReentrantLock内部提供Sync内部类继承AQS同步器,基于state状态标识获取锁的次数,提供公平锁和非公平锁两种实现,公平锁情况下, 调用AQS的acquire()方法,然后在AQS中尝试获取锁,如果当前队列中没有其他等待的线程节点,就让当前线程尝试获取锁,如果当前队列中还有其他节点或者当前线程获取锁失败,并且当前线程是AbstractOwnerableSynchronizer中之前设置过的独占锁的话,记录重入次数,返回true,如果返回失败会将当前线程添加进队列中,并再次尝试获取锁。