ReentrantLock 提供了公平锁和非公平锁,只须要在构造方法中使用一个 boolean
参数便可。默认非公平锁。java
今天从源码层面看看区别和具体实现。ui
ReentrantLock
内部有一个抽象类 Sync
,继承了 AQS。spa
而公平锁的实现就是 FairSync
,非公平锁的实现就是 NodFairSync
。线程
两把锁的区别在于lock
方法的实现。设计
final void lock() {
acquire(1);
}
复制代码
调用的是 AQS 的acquire
方法,熟悉 AQS 的同窗都知道,AQS 会回调子类的 tryAcquire
方法,看看公平锁的tryAcquire
实现。code
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;
}
复制代码
说下逻辑:cdn
注意上面的第二步:判断 AQS 队列中是否有等待的线程。blog
这就是公平的体现。继承
再看看非公平锁的区别。队列
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
复制代码
lock 方法就不一样了,很冲动的一个方法,直接使用 CAS 获取锁,若是成功了,就设置锁的持有线程为本身。很快速。因此默认使用了非公平锁。
若是失败了,就调用 AQS 的 acquire
方法。固然,咱们看的仍是tryAcquire
方法,在上面的代码中,tryAcquire
方法调用了父类Sync
的 nonfairTryAcquire
,为何在父类中呢?
在ReentrantLock
的 tryLock
方法中,也调用了该方法。由于这个方法是快速返回的。该方法不会让等待时间久的线程获取锁。符合 tryLock
的设计。
方法实现以下:
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;
}
复制代码
该方法相比较公平锁的 tryAcquire
方法,少了一步判断 AQS 队列中是否有等待的线程的操做。
他要作的就是直接抢锁,不让给队列里那些等待时间长的。
抢不到再进入队列。等待他的前置节点唤醒他。这个过程是公平的。
ReentrantLock
中的公平锁和非公平锁的区别就在于:调用 lock
方法获取锁的时候 要不要判断 AQS 队列中是否有等待的线程
,公平锁为了让每个线程都均衡的使用锁,就须要判断,若是有,让给他,非公平锁很霸道,不让不让就不让。
但若是失败了,进入队列了,进会按照 AQS 的逻辑来,总体顺序就是公平的。
还有个注意的地方就是:ReentrantLock
的 tryLock(无超时机制)
方法使用的非公平策略。符合他的设计。
而 tryLock(long timeout, TimeUnit unit)
方法则会根据 Sync 的具体实现来调用。不会冲动的调用 nonfairTryAcquire
方法。