Java并发编程之ReentrantLock源码(四)
前言
前面三篇文章介绍了ReentrantLock的加锁和释放锁的原理,这篇文章通过源码来了解下ReentrantLock的一些特性是如何实现的,主要包括以下三点:
- 可重入
- 可中断
- 公平锁
可重入
// NonFairSync
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;
}
其实前面介绍加锁流程的时候已经提到了重入的流程,这部分代码在ReentrantLock内部类Sync的nonfairTryAcquire方法中。
注意看这里的else if语句,如果state不是0表示有线程已经加锁了,这里判断如果当前线程就是持有锁的线程,就把state的值加1,也就是当前线程获取锁成功之后又获取锁,这就是可重入的意思,每重入一次就把state加上1。相对应的,在释放锁时重入几次就需要对state减几次1,直到state为0才表示释放了锁。
可中断
// ReentrantLock类
public void lockInterruptibly() throws InterruptedException {
sync.acquireInterruptibly(1);
}
// AQS类
public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}
// AQS类
private void doAcquireInterruptibly(int arg) throws InterruptedException {
final Node node = addWaiter(Node.EXCLUSIVE);
boolean failed = true;
try {
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
throw new InterruptedException();
}
} finally {
if (failed)
cancelAcquire(node);
}
}
可中断调用的是ReentrantLock的lockInterruptibly,最终会调用AQS的doAcquireInterruptibly方法,源码如上。
在Java并发编程之ReentrantLock源码(二)这篇文章中分析过获取锁的流程,通过前面的介绍我们了解到线程获取锁失败时就会调用park方法进行阻塞,而此时如果调用了线程的interrupt方法,就会打断park继续执行,parkAndCheckInterrupt方法就会返回Thread.interrupted()也就是true,所以就会执行抛出异常的代码来响应中断。
公平锁
// ReentrantLock#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;
}
上面是非公平锁获取锁的代码,如果state等于0也就是此刻是无锁状态,当前线程就直接尝试CAS修改state来获取锁,而阻塞队列是否有等待的线程这里并没有考虑,这也就是非公平的体现。
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;
}
这是公平锁获取锁的代码,我们看到如果state等于0,这里多了一层判断hasQueuedPredecessors,来看下源码
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t && ((s = h.next) == null || s.thread != Thread.currentThread());
}
h!=t:表示头节点和尾节点指向的不是同一个节点,也就是队列中有其他节点;(s = h.next) == null:表示head的后继节点是null,有可能是头节点还没有指向下一个节点,个人理解参考下面代码,通过CAS设置了tail节点,但是head节点还没有把next指向下一个节点。
if (compareAndSetTail(pred, node)) {
pred.next = node;
return node;
}
s.thread != Thread.currentThread():表示head节点的后继节点不是当前线程的节点。 所以如果队列中有节点,并且第二个节点是null或者不是当前线程的节点,那么当前线程就不能去获取锁以示公平。
转载自:https://juejin.cn/post/7229310860732268601