1 类图结构
ReentrantLock是JUC包下的一个独占锁,类图结构如下:
可以看到,其实现了Lock接口,并且底层是通过AQS抽象同步队列来实现的,我们直到AQS中有一个state状态变量,在ReentrantLock,state = 0时表示该锁还未被任何线程持有,大于1时则表示该锁已经被线程所持有,并且表示了该锁的重入次数。其中Sync是该类的一个内部类,NonfairSync和FaiSync分别对应获取锁的非公平与公平策略。对于这个锁,我们常用的就是lock与unlock方法,再就是Condition的应用,本文主要对这三者进行分析
2 lock()
该方法是获取锁的方法,具体代码如下所示:
public void lock() {
sync.lock();
}
可以看到实质上是调用内部类sync的lock方法,sync的lock()方法又有两种实现,分别是公平策略与非公平策略
2.1 非公平策略
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
首先通过CAS操作将state置1,若CAS操作成功则直接将给线程设置为该锁的持有者,否则调用acquire(1)方法,该方法是AQS中的方法,具体实现如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
该方法中首先通过tryAcquire()方法来获取资源,获取失败则将其挂到AQS队列的尾部,并调用LockSupport的lock()方法将自己挂起,tryAcquire()方法的实现如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
调用Sync类中的nonfairTryAcquire(acquires)方法,实现如下:
final boolean nonfairTryAcquire(int acquires) {
//获取当前线程
final Thread current = Thread.currentThread();
//获取当前的state值
int c = getState();
if (c == 0) { //若state值为0则通过CAS操作来设置state值,设置成功则将当前线程设置为锁的持有者
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
//state不为0则看当前线程是否是该锁的持有者,是则将增加其重入次数
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error("Maximum lock count exceeded");
setState(nextc);
return true;
}
//当前线程设置state值失败并且不是该锁的持有者则直接返回false,表示获取锁失败了
return false;
}
2.1 公平策略
公平策略与非公平策略最大的不同就是在tryAcquire()方法的实现上,公平策略的实现如下
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;
}
可以看到多了一个hasQueuedPredecessors()的判断,该函数就是用来判断当前线程是否前驱节点,也就是AQS队列中是否有线程在等待获取锁,若有,则将当前线程封装到Node节点挂到AQS队列尾部后将自己挂起,若无则和非公平策略的代码是一样的了。
3 unlock()
public void unlock() {
sync.release(1);
}
调用了AQS的release()方法
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
调用tryRelease()方法尝试释放资源,释放成功则唤醒队列中头结点中的线程,然后该线程再去尝试获取资源,tryRelease()方法的实现如下:
protected final boolean tryRelease(int releases) {
//计算release后的state值
int c = getState() - releases;
//当前线程若不是该锁的持有者则抛出异常
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
//若release后的state值为0则需要将锁完全释放了
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
//若release后的state值不为0则为减少该锁的重入次数
setState(c);
return free;
}
4 条件变量支持
相关操作可移步到前面的一片文章:Java并发编程—锁机制之抽象同步队列AQS