公平鎖是指多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖盼理,線程直接進(jìn)入隊(duì)列中排隊(duì)紊浩,隊(duì)列中的第一個(gè)線程才能獲得鎖蔫饰。公平鎖的優(yōu)點(diǎn)是等待鎖的線程不會(huì)餓死。缺點(diǎn)是整體吞吐效率相對(duì)非公平鎖要低牌里,等待隊(duì)列中除第一個(gè)線程以外的所有線程都會(huì)阻塞颊咬,CPU喚醒線程的開銷比非公平鎖要大。
非公平鎖是多個(gè)線程加鎖時(shí)直接嘗試獲取鎖牡辽,獲取不到才會(huì)到等待隊(duì)列的隊(duì)尾等待喳篇。但如果此時(shí)鎖剛好可用,那么這個(gè)線程可以無需阻塞直接獲取到鎖态辛,所以非公平鎖有可能出現(xiàn)后申請(qǐng)先獲取鎖的場景麸澜。非公平鎖的優(yōu)點(diǎn)是可以減少喚起線程的開銷,整體的吞吐效率高奏黑,因?yàn)榫€程有幾率不阻塞直接獲得鎖炊邦,CPU不必喚醒所有線程编矾。缺點(diǎn)是處于等待隊(duì)列中的線程可能會(huì)餓死,或者等很久才會(huì)獲得鎖馁害。
直接用語言描述可能有點(diǎn)抽象窄俏,這里作者用從別處看到的一個(gè)例子來講述一下公平鎖和非公平鎖。
如上圖所示碘菜,假設(shè)有一口水井凹蜈,有管理員看守,管理員有一把鎖忍啸,只有拿到鎖的人才能夠打水仰坦,打完水要把鎖還給管理員。每個(gè)過來打水的人都要管理員的允許并拿到鎖之后才能去打水计雌,如果前面有人正在打水悄晃,那么這個(gè)想要打水的人就必須排隊(duì)。管理員會(huì)查看一下要去打水的人是不是隊(duì)伍里排最前面的人凿滤,如果是的話妈橄,才會(huì)給你鎖讓你去打水;如果你不是排第一的人鸭巴,就必須去隊(duì)尾排隊(duì)眷细,這就是公平鎖。
但是對(duì)于非公平鎖鹃祖,管理員對(duì)打水的人沒有要求。即使等待隊(duì)伍里有排隊(duì)等待的人普舆,但如果在上一個(gè)人剛打完水把鎖還給管理員而且管理員還沒有允許等待隊(duì)伍里下一個(gè)人去打水時(shí)恬口,剛好來了一個(gè)插隊(duì)的人,這個(gè)插隊(duì)的人是可以直接從管理員那里拿到鎖去打水沼侣,不需要排隊(duì)祖能,原本排隊(duì)等待的人只能繼續(xù)等待。如下圖所示:
接下來我們通過ReentrantLock的源碼來講解公平鎖和非公平鎖蛾洛。
根據(jù)代碼可知养铸,ReentrantLock里面有一個(gè)內(nèi)部類Sync,Sync繼承AQS(AbstractQueuedSynchronizer)轧膘,添加鎖和釋放鎖的大部分操作實(shí)際上都是在Sync中實(shí)現(xiàn)的钞螟。它有公平鎖FairSync和非公平鎖NonfairSync兩個(gè)子類。ReentrantLock默認(rèn)使用非公平鎖谎碍,也可以通過構(gòu)造器來顯示的指定使用公平鎖鳞滨。
下面我們來看一下公平鎖與非公平鎖的加鎖方法的源碼:
通過上圖中芙蓉源代碼對(duì)比,我們可以明顯的看出公平鎖與非公平鎖的lock()方法唯一的區(qū)別就在于公平鎖在獲取同步狀態(tài)時(shí)多了一個(gè)限制條件:hasQueuedPredecessors()蟆淀。
再進(jìn)入hasQueuedPredecessors()拯啦,可以看到該方法主要做一件事情:主要是判斷當(dāng)前線程是否位于同步隊(duì)列中的第一個(gè)澡匪。如果是則返回true,否則返回false褒链。
綜上唁情,公平鎖就是通過同步隊(duì)列來實(shí)現(xiàn)多個(gè)線程按照申請(qǐng)鎖的順序來獲取鎖,從而實(shí)現(xiàn)公平的特性甫匹。非公平鎖加鎖時(shí)不考慮排隊(duì)等待問題荠瘪,直接嘗試獲取鎖,所以存在后申請(qǐng)卻先獲得鎖的情況赛惩。