先来回顾下公平锁的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 出现误判会怎么样呢?公平锁是不是就不公平了呀。那我们来研究下hasQueuedPredecessors ,是不是真有百密一疏的情况。
假如线程1已经持有锁了。这个该时候线程2来获取锁,走到hasQueuedPredecessors() 返回false,接着往后面执行CAS ,肯定执行失败,因为现在锁被线程1占有,返回aquire 方法。
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
接着走就是走到addWaiter 的enq 方法。线程2进入等待队列。
private Node enq(final Node node) {
for (;;) {
Node t = tail;
if (t == null) {
if (compareAndSetHead(new Node()))
tail = head;
} else {
node.prev = t;
if (compareAndSetTail(t, node)) {
t.next = node;
return t;
}
}
}
}
假设线程2走到注释截至处,现在有一个线程3来抢锁。当它判断等待队列时hasQueuedPredecessors 会返回false,因为h==t 。
public final boolean hasQueuedPredecessors() {
Node t = tail;
Node h = head;
Node s;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
那么问题来了,实际上等待队列中应该有线程2,现在我们却得出了等待队列为空的判断呀,线程3不的直接走CAS 吗,万一线程1刚好释放锁了,线程3就插队了阿。由此可见,公平锁并不公平。
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 是否会出现误判。
|