先来回顾下公平锁的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) { // 此时等head、tail为空,满足`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
是否会出现误判。