前面几篇文章介绍了AQS,也说了ReentrantLock 加锁解锁源码
现在说说,公平锁与非公平锁,到底哪里不同
ReentrantLock公平锁与非公平锁的不同
前面几篇文章,是以非公平锁为例子来说明的,公平锁实现与这类似。
先记住差别,前面也说过了,
公平锁,等待线程不为空,只有入队才可以抢锁
非公平锁,等待线程不为空,也可以抢锁。
现在看原码级别的实现
ReentrantLock lock = new ReentrantLock(true); // 传入参数 true 即是公平锁
final void lock() { // 公平锁lock方法,直接调用acquire(), 非公平锁是先抢锁,失败用调用acquire()
acquire(1);
}
公平锁与非公平锁,调用的 actuire() 方法是一样的,不一样的是tryAcquire()
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 公平锁tryAcquire()逻辑,与非公平锁区别是多了!hasQueuedPredecessors() 这个判断
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() ,看下它的原码
public final boolean hasQueuedPredecessors() {
// The correctness of this depends on head being initialized
// before tail and on head.next being accurate if the current
// 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());
}
这个方法很精炼,直接说逻辑,
- 若等待队列为空,即未初始化,返回 false;
- 若等待队列已初始化,哨兵结点没有后继结点,返回false;
- 若哨兵结点有后继结点,后继结点的线程是当前线程,返回false;
- 其它情况返回 true
返回false就是可以抢锁。
理解了这个之后,再看 tryAcquire()
if (c == 0) { // state 是 0 ,可以抢锁
if (!hasQueuedPredecessors() && // 等待队列中有线程在等待时,只有头节点的后继线程可抢锁,其它没资格。
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
由此看出,tryAcquire() 方法保证了,抢锁时,若等待队列中有线程在等待,外来的线程就不能抢锁,只能先入队,即排在后面。
公平锁与非公平锁的差别,就这么多,如果前面几篇文章理解了,今天这篇就很简单了。
上一篇: ReentrantLock 解锁源码剖析