一、概念理解
公平锁:申请所的时候排队,谁也不插队
非公平锁:申请的时候插队(先插队,不行了再排队)
二、差别
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流程

本文详细解析了公平锁与非公平锁的概念及其在ReentrantLock中的实现差异。公平锁遵循先进先出原则,非公平锁则允许线程在等待队列中尝试抢占锁,若失败则加入队列。通过对比两者在申请锁时的行为,揭示了它们在效率与公平性之间的权衡。
公平锁与非公平锁&spm=1001.2101.3001.5002&articleId=100111397&d=1&t=3&u=18ceecbe25d742c5905201bf0f872e6d)
990

被折叠的 条评论
为什么被折叠?



