上一篇文章《详解AQS二:ReentrantLock公平锁原理》中,详细分析了ReentrantLock公平锁的AQS实现原理,本篇文章将会继续分析ReentrantLock非公平锁的实现原理。
首先看看非公平锁NonfairSync的源码,以方便分析和公平锁FairSync的区别
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
/**
* 区别一:获取锁的时候在调用acquire获取锁之前允许先使用CAS方式直接获取锁
* 无需进入队列等待
*/
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
nonfairTryAcquire方法调用的是Syn中的方法
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
/**
* 区别二:这里的if条件没有再判定!hasQueuedPredecessors(),
* 这样即使节点前面有正在等待的节点,它也会不管不顾的去抢占锁资源
*/
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;
}
上面的代码注释已经写的很清楚,ReentrantLock的公平锁和非公平锁的区别全部都在NonfairSync
和FairSync
的抢占锁代码的区别上:
- lock方法:非公平锁上来就尝试CAS抢占锁,不管现在AQS队列中到底有没有线程在排队。这显然对正在队列中等待获取锁的线程不公平。
- tryAcquire方法:这是AQS中定义的钩子方法,在当前锁空闲的情况下,非公平锁也是上来就CAS抢占锁丝毫不管正在排队的AQS队列中的线程,而公平锁却要先调用
hasQueuedPredecessors
方法判定当前节点前面还有没有线程在排队,如果有线程等在当前线程的前面,则不允许抢占锁,乖乖去排队。
所以非公平锁和公平锁和AQS无关,仅仅是实现类中lock方法和tryAcquire方法的实现区别。
那么,公平锁和非公平锁的效率谁更高?
使用JMH微基准测试(详情参考《微基准测试工具JMH》)测试使用ReentrantLock公平锁和非公平锁对Long类型自增一亿次所用时间,比较结果如下:
锁类型 | 自增一亿次所用时间(秒) |
---|---|
FairSync | 2273.097(误差68.516秒) |
NonfairSync | 170.939(误差25.037秒) |
可以看到公平锁效率虽然公平,但是它的执行效率在高并发效率下要比非公平锁效率低很多,非公平锁效率大概是公平锁效率的13
倍。
由此可见,在代码的世界里,公平不一定是件好事啊。。。。
注意:本文归作者所有,未经作者允许,不得转载