目录
背景知识
ReentrantLock的组成
概述
公平锁示意图
非公平锁示意图
源码解读
非公平锁
公平锁
代码对比
问题
知识扩展
tryLock方法
参考资料
首先看下ReentrantLock的组成结构。
公平锁和非公平锁主要是通过内部类FairSync和NonFairSync中的tryAquire()方法来区分的。
ReentrantLock中的公平锁和非公平锁的主要区别为
注意: 以上图片中的Head是一个特殊的节点, 仅用于构造等待队列这个数据结构, 不包含等待的线程信息, 所以下一个可以获取锁资源的节点是Node1节点。
static final class NonfairSync extends Sync {
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
Sync中的nonfairTryAcquire()方法。
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// flag 1 公平锁和非公平锁的主要不同点
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;
}
其中nonfairTryAcquire()方法是Sync类中实现的,注意flag 1处的代码, 只要state=0, 当前线程就有机会抢占该锁资源, 不考虑等待队列中是否已经有等待的线程。
static final class FairSync extends Sync {
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// flag 2 公平锁和非公平锁的主要区别
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;
}
}
flag 2处的代码显示, 竞争锁资源的前提条件是!hasQueuedpredecessord(), 即当前队列中没有其他线程在等待锁资源, 代码如下:
public final boolean hasQueuedPredecessors() {
//he correctness of this depends on head being initialized
//before tail and on head.next being accurate if thecurrent
//thread is first in queue.
Node t = tail; //Read fields in reverse initialization order
Node h = head;
Node s;
return h != t &&
(
(s = h.next) == null ||
s.thread != Thread.currentThread()
);
}
简单解释一下这段代码:
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) { // Must initialize
if (compareAndSetHead(new Node()))
// Head节点已经创建, 初始化操作完成了一半, tail=head尚未来执行,
// 到此已经能确定队列中存在等待锁资源的线程
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
总之, hasQueuedPredecessors()方法只有返回false,当前线程才有资格尝试获取锁资源:假如返回true, 则意味着他必须排队等待。
公平锁
if (c == 0) {
// flag 2 公平锁和非公平锁的主要区别
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
非公平锁
if (c == 0) {
// flag 1 公平锁和非公平锁的主要区别
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
阅读ReenTrantLock.FairSync.tryAcquire()方法,遇到两个有意思的问题:
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
// flag 3 设置state使用了CAS
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
// flag 4 竞争的线程总数查过Int的最大值, 直接报错
throw new Error("Maximum lock count exceeded");
// flag 5 同样是设置state值, 此处却没有使用CAS
setState(nextc);
return true;
}
return false;
}
同样是state状态,flag 3 处使用了CAS, flag 5 处却没有使用CAS, 这是为啥呢? 因为当前线程为已持有锁的线程时(current == getExclusiveOwnerThread()), 只有他自己能修改state状态, 所以无需考虑并发修改的情况;
flag 4 处竞争的线程总数超过int的最大值时, 会直接报错(抛出"Maximum lock count exceeded"异常), 然后在finally代码块中执行cancelAquire()操作, 将该线程对应的节点移出队列, 最后将异常抛给调用方, 调用方线程停止运行, 如下所示:
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
//flag 6
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
这里同时补充一点, 不管Reentrant使用公平锁(FairLock)还是非公平锁(NonFairLock), ReentrantLock类的tryLock()方法都会调用Sync类的nonfairTryAcquire()方法,采取非公平的竞争策略。
《公平锁与非公平锁》
《一些比较难看懂的方法》