公平鎖和非公平鎖的區(qū)別

正文

開始聊之前,我先大概說一下他們兩者的定義,幫大家回顧或者認識一下。

公平鎖:多個線程按照申請鎖的順序去獲得鎖,線程會直接進入隊列去排隊,永遠都是隊列的第一位才能得到鎖。

  • 優(yōu)點:所有的線程都能得到資源,不會餓死在隊列中。
  • 缺點:吞吐量會下降很多,隊列里面除了第一個線程,其他的線程都會阻塞,cpu喚醒阻塞線程的開銷會很大。

非公平鎖:多個線程去獲取鎖的時候,會直接去嘗試獲取,獲取不到,再去進入等待隊列,如果能獲取到,就直接獲取到鎖。

  • 優(yōu)點:可以減少CPU喚醒線程的開銷,整體的吞吐效率會高點,CPU也不必取喚醒所有線程,會減少喚起線程的數(shù)量。
  • 缺點:你們可能也發(fā)現(xiàn)了,這樣可能導致隊列中間的線程一直獲取不到鎖或者長時間獲取不到鎖,導致餓死。

我舉個例子給他家通俗易懂的講一下的,想了好幾天終于在前天跟三歪去肯德基買早餐排隊的時候發(fā)現(xiàn)了怎么舉例了。

現(xiàn)在是早餐時間,敖丙想去kfc搞個早餐,發(fā)現(xiàn)有很多人了,一過去沒多想,就乖乖到隊尾排隊,這樣大家都覺得很公平,先到先得,所以這是公平鎖咯。

那非公平鎖就是,敖丙過去買早餐,發(fā)現(xiàn)大家都在排隊,但是敖丙這個人有點渣的,就是喜歡插隊,那他就直接懟到第一位那去,后面的雞蛋,米豆都不行,我插隊也不敢說什么,只能默默忍受了。

但是偶爾,雞蛋也會崛起,叫我滾到后面排隊,我也是欺軟怕硬,默默到后面排隊,就插隊失敗了。

介紹完簡單的例子,大家可能會說,渣丙,這個我也知道的啊。

我們是不是應該回歸真正的實現(xiàn)了,其實在大家經(jīng)常使用的ReentrantLock中就有相關(guān)公平鎖,非公平鎖的實現(xiàn)了。

大家還記得我在樂觀鎖、悲觀鎖章節(jié)提到的Sync類么,是ReentrantLock他本身的一個內(nèi)部類,他繼承了AbstractQueuedSynchronizer,我們在操作鎖的大部分操作,都是Sync本身去實現(xiàn)的。

Sync呢又分別有兩個子類:FairSync和NofairSync

他們子類的名字就可以見名知意了,公平和不公平那又是怎么在代碼層面體現(xiàn)的呢?

公平鎖:

你可以看到,他加了一個hasQueuedPredecessors的判斷,那他判斷里面有些什么玩意呢?

代碼的大概意思也是判斷當前的線程是不是位于同步隊列的首位,是就是返回true,否就返回false。

我總覺得寫到這里就應該差不多了,但是我坐下來,靜靜的思考之后發(fā)現(xiàn),還是差了點什么。

上次聊過ReentrantLock了,但是AQS什么的我都只是提了一嘴,一個線程進來,他整個處理鏈路到底是怎樣的呢?

公平鎖到底公平不公平呢?讓我們一起跟著丙丙走進ReentrantLock的內(nèi)心世界。

上面提了這么多,我想你應該是有所了解了,那一個線程進來ReentrantLock這個渣男是怎么不公平的呢?(默認是非公平鎖)

我先畫個圖,幫助大家了解下細節(jié):

ReentrantLock的Sync繼承了AbstractQueuedSynchronizer也就是我們常說的AQS

他也是ReentrantLock加鎖釋放鎖的核心,大致的內(nèi)容我之前一期提到了,我就不過多贅述了,他們看看一次加鎖的過程吧。

A線程準備進去獲取鎖,首先判斷了一下state狀態(tài),發(fā)現(xiàn)是0,所以可以CAS成功,并且修改了當前持有鎖的線程為自己。

這個時候B線程也過來了,也是一上來先去判斷了一下state狀態(tài),發(fā)現(xiàn)是1,那就CAS失敗了,真晦氣,只能乖乖去等待隊列,等著喚醒了,先去睡一覺吧。

A持有久了,也有點膩了,準備釋放掉鎖,給別的仔一個機會,所以改了state狀態(tài),抹掉了持有鎖線程的痕跡,準備去叫醒B。

這個時候有個帶綠帽子的仔C過來了,發(fā)現(xiàn)state怎么是0啊,果斷CAS修改為1,還修改了當前持有鎖的線程為自己。

B線程被A叫醒準備去獲取鎖,發(fā)現(xiàn)state居然是1,CAS就失敗了,只能失落的繼續(xù)回去等待隊列,路線還不忘罵A渣男,怎么騙自己,欺騙我的感情。

諾以上就是一個非公平鎖的線程,這樣的情況就有可能像B這樣的線程長時間無法得到資源,優(yōu)點就是可能有的線程減少了等待時間,提高了利用率。

現(xiàn)在都是默認非公平了,想要公平就得給構(gòu)造器傳值true。

 ReentrantLock lock = new ReentrantLock(true);

說完非公平,那我也說一下公平的過程吧:

線A現(xiàn)在想要獲得鎖,先去判斷下state,發(fā)現(xiàn)也是0,去看了看隊列,自己居然是第一位,果斷修改了持有線程為自己。

線程b過來了,去判斷一下state,嗯哼?居然是state=1,那cas就失敗了呀,所以只能乖乖去排隊了。

線程A暖男來了,持有沒多久就釋放了,改掉了所有的狀態(tài)就去喚醒線程B了,這個時候線程C進來了,但是他先判斷了下state發(fā)現(xiàn)是0,以為有戲,然后去看了看隊列,發(fā)現(xiàn)前面有人了,作為新時代的良好市民,果斷排隊去了。

線程B得到A的召喚,去判斷state了,發(fā)現(xiàn)值為0,自己也是隊列的第一位,那很香呀,可以得到了。

總結(jié):

總結(jié)我不說話了,但是去獲取鎖判斷的源碼,箭頭所指的位置,現(xiàn)在是不是都被我合理的解釋了,當前線程,state,是否是0,是否是當前線程等等,都去思考下。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內(nèi)容