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