1、什么是公平鎖與非公平鎖
公平鎖:公平鎖就是保障了多線程下各線程獲取鎖的順序埋虹,先到的線程優(yōu)先獲取鎖猜憎。
非公平鎖:非公平鎖則無法提供這個保障(先到的線程優(yōu)先獲取鎖)。
2搔课、ReentrantLock如何實現(xiàn)公平與非公平
Java并發(fā)包下面的ReentrantLock胰柑、ReadWriteLock默認(rèn)都是非公平模式。
下面我們就來一起看看ReentrantLock是如何實現(xiàn)公平與非公平的爬泥。
ReentrantLock實現(xiàn)了Lock接口柬讨。提供了下面2個構(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ū)別:沒有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的源碼境输。該方法返回“隊列中是否存在一個線程(先于當(dāng)前線程)”蔗牡,如果存在話颖系,當(dāng)前線程就要加入到隊列的尾部。
* @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ū)別更明顯嘁扼。
公平鎖就是在獲取鎖之前會先判斷等待隊列是否為空或者自己是否位于隊列頭部,該條件通過才能繼續(xù)獲取鎖黔攒。
在結(jié)合兔子喝水的圖分析趁啸,非公平鎖獲取所得順序基本決定在9、10督惰、11這三個事件發(fā)生的先后順序不傅,
- 1、若在釋放鎖的時候總是沒有新的兔子來打擾赏胚,則非公平鎖等于公平鎖访娶;
- 2、若釋放鎖的時候栅哀,正好一個兔子來喝水震肮,而此時位于隊列頭的兔子還沒有被喚醒(因為線程上下文切換是需要不少開銷的)称龙,此時后來的兔子則優(yōu)先獲得鎖留拾,成功打破公平,成為非公平鎖鲫尊;
其實對于非公平鎖痴柔,只要線程進入了等待隊列,隊列里面依然是FIFO的原則疫向,跟公平鎖的順序是一樣的咳蔚。因為公平鎖與非公平鎖的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搔驼、公平鎖與非公平鎖性能對比
非公平鎖的效率高于公平鎖:上文說到的線程切換的開銷谈火,其實就是非公平鎖效率高于公平鎖的原因,因為非公平鎖減少了線程掛起的幾率舌涨,后來的線程有一定幾率逃離被掛起的開銷糯耍。
滬漂程序員一枚。
堅持寫博客囊嘉,如果覺得還可以的話温技,給個小星星哦,你的支持就是我創(chuàng)作的動力扭粱。
推薦閱讀:
Java內(nèi)存模型-volatile的應(yīng)用(實例講解)
synchronized解決原子性-synchronized的三種應(yīng)用方式(實例講解)
線程池-一文弄懂Java里面的線程池ThreadPoolExecutor
可重入鎖-面試題:synchronized是可重入鎖嗎
本文參考:一張圖讀懂非公平鎖與公平鎖