Java多線程系列--“JUC鎖”05之 非公平鎖
獲取非公平鎖(基於JDK1.7.0_40)
非公平鎖和公平鎖在獲取鎖的方法上,流程是一樣的;它們的區別主要表現在“嘗試獲取鎖的機制不同”。簡單點說,“公平鎖”在每次嘗試獲取鎖時,都是采用公平策略(根據等待隊列依次排序等待);而“非公平鎖”在每次嘗試獲取鎖時,都是采用的非公平策略(無視等待隊列,直接嘗試獲取鎖,如果鎖是空閑的,即可獲取狀態,則獲取鎖)。
在前面的“Java多線程系列--“JUC鎖”03之 公平鎖(一)”中,已經詳細介紹了獲取公平鎖的流程和機制;下面,通過代碼分析以下獲取非公平鎖的流程。
1. lock()
lock()在ReentrantLock.java的NonfairSync類中實現,它的源碼如下:
final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}
說明:
lock()會先通過compareAndSet(0, 1)來判斷“鎖”是不是空閑狀態。是的話,“當前線程”直接獲取“鎖”;否則的話,調用acquire(1)獲取鎖。
(01) compareAndSetState()是CAS函數,它的作用是比較並設置當前鎖的狀態。若鎖的狀態值為0,則設置鎖的狀態值為1。
(02) setExclusiveOwnerThread(Thread.currentThread())的作用是,設置“當前線程”為“鎖”的持有者。
“公平鎖”和“非公平鎖”關於lock()的對比
公平鎖 -- 公平鎖的lock()函數,會直接調用acquire(1)。
非公平鎖 -- 非公平鎖會先判斷當前鎖的狀態是不是空閑,是的話,就不排隊,而是直接獲取鎖。
2. acquire()
acquire()在AQS中實現的,它的源碼如下:
public final void acquire(int arg) {
if (!tryAcquire(arg) &&
acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
selfInterrupt();
}
(01) “當前線程”首先通過tryAcquire()嘗試獲取鎖。獲取成功的話,直接返回;嘗試失敗的話,進入到等待隊列依次排序,然後獲取鎖。
(02) “當前線程”嘗試失敗的情況下,會先通過addWaiter(Node.EXCLUSIVE)來將“當前線程”加入到"CLH隊列(非阻塞的FIFO隊列)"末尾。
(03) 然後,調用acquireQueued()獲取鎖。在acquireQueued()中,當前線程會等待它在“CLH隊列”中前面的所有線程執行並釋放鎖之後,才能獲取鎖並返回。如果“當前線程”在休眠等待過程中被中斷過,則調用selfInterrupt()來自己產生一個中斷。
“公平鎖”和“非公平鎖”關於acquire()的對比
公平鎖和非公平鎖,只有tryAcquire()函數的實現不同;即它們嘗試獲取鎖的機制不同。這就是我們所說的“它們獲取鎖策略的不同所在之處”!
在“Java多線程系列--“JUC鎖”03之 公平鎖(一)”中,已經詳細介紹了acquire()涉及到的各個函數。這裏僅對它們有差異的函數tryAcquire()進行說明。
非公平鎖的tryAcquire()在ReentrantLock.java的NonfairSync類中實現,源碼如下:
protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}
nonfairTryAcquire()在ReentrantLock.java的Sync類中實現,源碼如下:
final boolean nonfairTryAcquire(int acquires) {
// 獲取“當前線程”
final Thread current = Thread.currentThread();
// 獲取“鎖”的狀態
int c = getState();
// c=0意味著“鎖沒有被任何線程鎖擁有”
if (c == 0) {
// 若“鎖沒有被任何線程鎖擁有”,則通過CAS函數設置“鎖”的狀態為acquires。
// 同時,設置“當前線程”為鎖的持有者。
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;
}
說明:
根據代碼,我們可以分析出,tryAcquire()的作用就是嘗試去獲取鎖。
(01) 如果“鎖”沒有被任何線程擁有,則通過CAS函數設置“鎖”的狀態為acquires,同時,設置“當前線程”為鎖的持有者,然後返回true。
(02) 如果“鎖”的持有者已經是當前線程,則將更新鎖的狀態即可。
(03) 如果不術語上面的兩種情況,則認為嘗試失敗。
“公平鎖”和“非公平鎖”關於tryAcquire()的對比
公平鎖和非公平鎖,它們嘗試獲取鎖的方式不同。
公平鎖在嘗試獲取鎖時,即使“鎖”沒有被任何線程鎖持有,它也會判斷自己是不是CLH等待隊列的表頭;是的話,才獲取鎖。
而非公平鎖在嘗試獲取鎖時,如果“鎖”沒有被任何線程持有,則不管它在CLH隊列的何處,它都直接獲取鎖。
釋放非公平鎖(基於JDK1.7.0_40)
非公平鎖和公平鎖在釋放鎖的方法和策略上是一樣的。
總結
公平鎖和非公平鎖的區別,是在獲取鎖的機制上的區別。表現在,在嘗試獲取鎖時 —— 公平鎖,只有在當前線程是CLH等待隊列的表頭時,才獲取鎖;而非公平鎖,只要當前鎖處於空閑狀態,則直接獲取鎖,而不管CLH等待隊列中的順序。
只有當非公平鎖嘗試獲取鎖失敗的時候,它才會像公平鎖一樣,進入CLH等待隊列排序等待。
參考:
http://www.cnblogs.com/skywang12345/p/3496651.html
Java多線程系列--“JUC鎖”05之 非公平鎖