ReentrantLock公平锁与非公平锁的实现原理
ReentrantLock是J.U.C包下的一个类,默认是非公平的
public class ReentrantLock implements Lock, java.io.Serializable {
// 内部类
private final Sync sync;
public ReentrantLock() {
// 默认使用非公平锁
sync = new NonfairSync();
}
// 可通过传入参数(true)的形式使用公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
其中内部类 Sync 继承了 AQS
abstract static class Sync extends AbstractQueuedSynchronizer {
//...
}
而 NonfairSync 与 FairSync 都是继承自 Sync,也均是内部类
static final class NonfairSync extends Sync
static final class FairSync extends Sync
一. 非公平锁实现原理
我们先来看一下我们常用的使用方式 ReentrantLock 实现,一般都是直接 new 一个对象,然后通过 lock() 方法获取锁,它是怎么实现的呢,代码如下:
// 1. 获取对象,默认非公平
public ReentrantLock() {
sync = new NonfairSync();
}
// 2. 调用lock() 方法
public void lock() {
sync.lock();
}
// 3. 调用非公平内部类实现的 Lock() 方法
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
final void lock() {
// 尝试获取锁
if (compareAndSetState(0, 1))
// 获取成功后设置当前线程信息
setExclusiveOwnerThread(Thread.currentThread());
else
// 未获取到锁则执行以下方法
acquire(1);
}
// 其他方法。。。
}
// acquire(1) 方法内部逻辑
public final void acquire(int arg) {
if (!tryAcquire(arg) && // 尝试获取锁
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))// 调用AQS 同步方法
selfInterrupt();
}
非公平锁的 acquire(int arg) ,逻辑比较简单,源码如下
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
/**
* Performs non-fair tryLock. tryAcquire is implemented in
* subclasses, but both need nonfair try for trylock method.
*/
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
// 若锁状态为0,则直接执行获取锁逻辑
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
// 若锁已经有线程执行,则判断是否为当前线程,若是则state + 1, 这也是可重入的实现
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;
}
二. 公平锁实现原理
接下来我们一起看下公平锁的源码有何不同
// 传入 true 则会使用公平锁实现 FairSync
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
static final class FairSync extends Sync {
// lock方法直接调用 acquire 方法
final void lock() {
acquire(1);
}
// ...
}
acquire 方法是公用的,主要看一下内部的 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;
}
}
相信大家都已经看出来了它们的差异,接下来我再把它们不同的地方圈出来
三. 公平与非公平锁的对比
何为公平,何为非公平,在这个标榜着人人平等的社会,排队买东西大家都比较熟悉,不管是谁都得排队,先的来先买(除特殊情况)。
ReentrantLock也是相同的思想,引入了阻塞队列来实现排队等候,先来先执行,后来则排队(FIFO),所以是公平的。
那是什么是非公平呢,有人插队嘛,对吧,都插队了还谈什么公平呢,ReentrantLock的非公平锁就是不考虑阻塞队列的情况下直接获取锁。
看看源码上的区别大家就一目了然了:
// 非公平锁
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
// 公平锁
final void lock() {
acquire(1);
}
非公平的进来先看看能不能拿到锁,如果拿到的话转身就溜,也不看看有没有其它线程在排队等待
再来看下 acquire 方法的流程
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
// 公平锁
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;
}
// 非公平锁
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
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;
}
可以看出来,非公平锁在 lock() 方法里多了直接获取锁的逻辑
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
在 tryAcquire(int acquires) 方法里少了判断阻塞的逻辑,而公平锁首先会考虑是否有线程在排序等候,主要方法 hasQueuedPredecessors()
// FairSync
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
// NonfairSync
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
锁的释放公平与非公平是一样的
如有错误的地方还望大家指正