1、什么是公平鎖與非公平鎖
公平鎖:公平鎖就是保障了多線程下各線程獲取鎖的順序,先到的線程優(yōu)先獲取鎖。
非公平鎖:非公平鎖則無(wú)法提供這個(gè)保障(先到的線程優(yōu)先獲取鎖)。
2、ReentrantLock如何實(shí)現(xiàn)公平與非公平
Java并發(fā)包下面的ReentrantLock、ReadWriteLock默認(rèn)都是非公平模式。
下面我們就來(lái)一起看看ReentrantLock是如何實(shí)現(xiàn)公平與非公平的。
ReentrantLock實(shí)現(xiàn)了Lock接口。提供了下面2個(gè)構(gòu)造方法
public ReentrantLock() {
sync = new NonfairSync();
}
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
默認(rèn)構(gòu)造的是非公平鎖NonfairSync。
NonfairSync和FairSync都是ReentrantLock的內(nèi)部類,且繼承ReentrantLock的內(nèi)部抽象類Sync。
// 公平鎖
protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 主要區(qū)別:有hasQueuedPredecessors方法
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;
}
}
// 非公平鎖
final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
// 主要區(qū)別:沒(méi)有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;
}
二者的主要去別在于是否有hasQueuedPredecessors方法,我們看下hasQueuedPredecessors的源碼。該方法返回“隊(duì)列中是否存在一個(gè)線程(先于當(dāng)前線程)”,如果存在話,當(dāng)前線程就要加入到隊(duì)列的尾部。
* @return {@code true} if there is a queued thread preceding the
* current thread, and {@code false} if the current thread
* is at the head of the queue or the queue is empty
* @since 1.7
*/
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;
return h != t &&
((s = h.next) == null || s.thread != Thread.currentThread());
}
網(wǎng)上有這一張很棒的圖:

結(jié)合這張圖,二者的區(qū)別更明顯。
公平鎖就是在獲取鎖之前會(huì)先判斷等待隊(duì)列是否為空或者自己是否位于隊(duì)列頭部,該條件通過(guò)才能繼續(xù)獲取鎖。
在結(jié)合兔子喝水的圖分析,非公平鎖獲取所得順序基本決定在9、10、11這三個(gè)事件發(fā)生的先后順序,
- 1、若在釋放鎖的時(shí)候總是沒(méi)有新的兔子來(lái)打擾,則非公平鎖等于公平鎖;
- 2、若釋放鎖的時(shí)候,正好一個(gè)兔子來(lái)喝水,而此時(shí)位于隊(duì)列頭的兔子還沒(méi)有被喚醒(因?yàn)榫€程上下文切換是需要不少開(kāi)銷的),此時(shí)后來(lái)的兔子則優(yōu)先獲得鎖,成功打破公平,成為非公平鎖;
其實(shí)對(duì)于非公平鎖,只要線程進(jìn)入了等待隊(duì)列,隊(duì)列里面依然是FIFO的原則,跟公平鎖的順序是一樣的。因?yàn)楣芥i與非公平鎖的release()部分代碼是共用AQS的代碼。
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
/**
* Wakes up node's successor, if one exists.
*/
private void unparkSuccessor(Node node) {
/*
* If status is negative (i.e., possibly needing signal) try
* to clear in anticipation of signalling. It is OK if this
* fails or if status is changed by waiting thread.
*/
int ws = node.waitStatus;
if (ws < 0)
compareAndSetWaitStatus(node, ws, 0);
/*
* Thread to unpark is held in successor, which is normally
* just the next node. But if cancelled or apparently null,
* traverse backwards from tail to find the actual
* non-cancelled successor.
*/
Node s = node.next;
if (s == null || s.waitStatus > 0) {
s = null;
for (Node t = tail; t != null && t != node; t = t.prev)
if (t.waitStatus <= 0)
s = t;
}
if (s != null)
LockSupport.unpark(s.thread);
}
3、公平鎖與非公平鎖性能對(duì)比
非公平鎖的效率高于公平鎖:上文說(shuō)到的線程切換的開(kāi)銷,其實(shí)就是非公平鎖效率高于公平鎖的原因,因?yàn)榉枪芥i減少了線程掛起的幾率,后來(lái)的線程有一定幾率逃離被掛起的開(kāi)銷。
滬漂程序員一枚。
堅(jiān)持寫博客,如果覺(jué)得還可以的話,給個(gè)小星星哦,你的支持就是我創(chuàng)作的動(dòng)力。
推薦閱讀:
Java內(nèi)存模型-volatile的應(yīng)用(實(shí)例講解)
synchronized解決原子性-synchronized的三種應(yīng)用方式(實(shí)例講解)
線程池-一文弄懂Java里面的線程池ThreadPoolExecutor
可重入鎖-面試題:synchronized是可重入鎖嗎
本文參考:一張圖讀懂非公平鎖與公平鎖