1 區別
原文鏈接:https://www.baidu.com/link?url=57aywD0Q6WTnl7XKbIHuEzg3thMUswU0N7WVgfgfFamFpH_BWPzQLISqZ997zrxNLPbPEPYyNKZxf-QYXhFlzV-XDXZQSrU_8vGCsFEDk2K&wd=&eqid=9b25c7e1003efe56000000045da080cb
通過分析ReentrantLock中的公平鎖和非公平鎖的實現,其中tryAcquire是公平鎖和非公平鎖實現的區別,下面的兩種類型的鎖的tryAcquire的實現,從中我們可以看出在公平鎖中,每一次的tryAcquire都會檢查CLH隊列中是否仍有前驅的元素,如果仍然有那么繼續等待,通過這種方式來保證先來先服務的原則;而非公平鎖,首先是檢查並設置鎖的狀態,這種方式會出現即使隊列中有等待的線程,但是新的線程仍然會與排隊線程中的對頭線程競爭(但是排隊的線程是先來先服務的),所以新的線程可能會搶占已經在排隊的線程的鎖,這樣就無法保證先來先服務,但是已經等待的線程們是仍然保證先來先服務的,所以總結一下公平鎖和非公平鎖的區別:
1、公平鎖能保證:老的線程排隊使用鎖,新線程仍然排隊使用鎖。
2、非公平鎖保證:老的線程排隊使用鎖;但是無法保證新線程搶占已經在排隊的線程的鎖。
公平鎖的tryAcquire
/** * 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(); if (c == 0) { // !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; }
非公平鎖的
/** * Performs non-fair tryLock. tryAcquire is implemented in * subclasses, but both need nonfair try for trylock method. */ final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); if (c == 0) { // 新的線程可能搶占已經排隊的線程的鎖的使用權 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; }
2 源碼解析
參考 https://www.cnblogs.com/java-zhao/p/5131544.html
簡化版的步驟:(非公平鎖的核心)
基於CAS嘗試將state(鎖數量)從0設置為1
A、如果設置成功,設置當前線程為獨占鎖的線程;
B、如果設置失敗,還會再獲取一次鎖數量,
B1、如果鎖數量為0,再基於CAS嘗試將state(鎖數量)從0設置為1一次,如果設置成功,設置當前線程為獨占鎖的線程;
B2、如果鎖數量不為0或者上邊的嘗試又失敗了,查看當前線程是不是已經是獨占鎖的線程了,如果是,則將當前的鎖數量+1;如果不是,則將該線程封裝在一個Node內,並加入到等待隊列中去。等待被其前一個線程節點喚醒。
簡化版的步驟:(公平鎖的核心)
獲取一次鎖數量,
B1、如果鎖數量為0,如果當前線程是等待隊列中的頭節點,基於CAS嘗試將state(鎖數量)從0設置為1一次,如果設置成功,設置當前線程為獨占鎖的線程;
B2、如果鎖數量不為0或者當前線程不是等待隊列中的頭節點或者上邊的嘗試又失敗了,查看當前線程是不是已經是獨占鎖的線程了,如果是,則將當前的鎖數量+1;如果不是,則將該線程封裝在一個Node內,並加入到等待隊列中去。等待被其前一個線程節點喚醒。