一、概念理解
公平锁:申请所的时候排队,谁也不插队
非公平锁:申请的时候插队(先插队,不行了再排队)
二、差别
ReentrantLock的公平锁与非公平锁的差别在于,内部的同步器不一样,lock()方法调用的是sync的lock(),分别是FairSync与NonfairSync。
sync的lock()内部调用了AQS的acquire,而AQS的acquire做了三件事,第一件是直接申请锁(tryAcquire),第二件是没申请下锁的话,就创建线程等待节点并添加到等待队列(addWaiter),第三件是将改节点的prev节点标记为SIGNAL,并挂起该线程,等待唤醒再申请锁(acquireQueued)。
FairSync与NonfairSync的区别在tryAcquire方法不同,如下代码及注释(文末有lock流程图,一眼看差别)
公平锁同步器
static final class FairSync extends Sync {
private static final long serialVersionUID = -3000897897090466540L;
final void lock() {
acquire(1);
}
/**
* Fair version of tryAcquire. Don't grant access unless
* recursive call or no waiters or is first.
*/
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//状态等于0,锁没有占用
if (c == 0) {
//当前队列无等待线程
if (!hasQueuedPredecessors() &&
//修改锁状态成功
compareAndSetState(0, acquires)) {
//设置当前占着锁的线程为当前线程
setExclusiveOwnerThread(current);
//获取锁成功
return true;
}
}
//状态不为0,锁被占用,占用锁的线程是当前线程(重入)
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error("Maximum lock count exceeded”);
//叠加状态(状态为记录当前线程占用锁的次数)
setState(nextc);
//获取锁成功
return true;
}
//获取锁失败
return false;
}
}
非公平锁同步器
static final class NonfairSync extends Sync {
private static final long serialVersionUID = 7316153563782823691L;
/**
* Performs lock. Try immediate barge, backing up to normal
* acquire on failure.
*/
final void lock() {
//直接修改锁状态为1,也就是先尝试申请一下看看能否成功
if (compareAndSetState(0, 1))
//设置当前占着锁的线程为当前线程,申请锁成功
setExclusiveOwnerThread(Thread.currentThread());
else
//acquire方法会调用tryAcquire
acquire(1);
}
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
}
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
//状态等于0,锁没有占用
if (c == 0) {
//修改锁状态为1
if (compareAndSetState(0, acquires)) {
//设置当前占着锁的线程为当前线程,申请锁成功
setExclusiveOwnerThread(current);
return true;
}
}
//状态不为0,锁被占用,占用锁的线程是当前线程(重入)
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;
}
ps:hasQueuedPredecessors方法
((s = h.next) == null || s.thread != Thread.currentThread()) 是为了过滤掉两种临界情况
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;
//h != t 这是必要条件
return h != t &&
//h.next == null 说明head存在,tail还未设置,该种情况出现在addWaiter添加第一个等待节点时出现,设置完head还未来得及设置tail
//s.thread != Thread.currentThread() 该情况是防止,当前线程刚被unpark,首次执行tryAcquire时,head还未替换成当前线程节点
((s = h.next) == null || s.thread != Thread.currentThread());
}
三、 白话总结
- 公平锁在申请的时候先看有线程在排队没,有的话就去排队,没有的话就申请锁(所有申请锁线程都满足FIFO)。
- 非公平锁在申请的时候,先插队看看(也就是直接申请锁),可以的话就申请成功,不行的话再去排队,排上队的线程获取锁顺序就不会变了(排上队的线程满足FIFO)。
附件:
非公平锁 lock流程图

公平锁 lock流程图

unlock流程
