面試官:說一下公平鎖和非公平鎖的區別?

點贊再看,養成習慣,微信搜索【三太子敖丙】關注這個互聯網苟且偷生的工具人。git

本文 GitHub github.com/JavaFamily 已收錄,有一線大廠面試完整考點、資料以及個人系列文章。github

前言

上次咱們提到了樂觀鎖和悲觀鎖,那咱們知道鎖的類型還有不少種,咱們今天簡單聊一下,公平鎖和非公平鎖兩口子,以及他們在咱們代碼中的實踐。web

正文

開始聊以前,我先大概說一下他們二者的定義,幫你們回顧或者認識一下。面試

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

  • 優勢:全部的線程都能獲得資源,不會餓死在隊列中。
  • 缺點:吞吐量會降低不少,隊列裏面除了第一個線程,其餘的線程都會阻塞,cpu喚醒阻塞線程的開銷會很大。

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

  • 優勢:能夠減小CPU喚醒線程的開銷,總體的吞吐效率會高點,CPU也沒必要取喚醒全部線程,會減小喚起線程的數量。
  • 缺點:大家可能也發現了,這樣可能致使隊列中間的線程一直獲取不到鎖或者長時間獲取不到鎖,致使餓死。

我舉個例子給他家通俗易懂的講一下的,想了好幾天終於在前天跟三歪去肯德基買早餐排隊的時候發現了怎麼舉例了。app

如今是早餐時間,敖丙想去kfc搞個早餐,發現有不少人了,一過去沒多想,就乖乖到隊尾排隊,這樣你們都以爲很公平,先到先得,因此這是公平鎖咯。工具

那非公平鎖就是,敖丙過去買早餐,發現你們都在排隊,可是敖丙這我的有點渣的,就是喜歡插隊,那他就直接懟到第一位那去,後面的雞蛋,米豆都不行,我插隊也不敢說什麼,只能默默忍受了。spa

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

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

咱們是否是應該回歸真正的實現了,其實在你們常用的ReentrantLock中就有相關公平鎖,非公平鎖的實現了。

你們還記得我在樂觀鎖、悲觀鎖章節提到的Sync類麼,是ReentrantLock他自己的一個內部類,他繼承了AbstractQueuedSynchronizer,咱們在操做鎖的大部分操做,都是Sync自己去實現的。

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

他們子類的名字就能夠見名知意了,公平和不公平那又是怎麼在代碼層面體現的呢?

公平鎖:

你能夠看到,他加了一個hasQueuedPredecessors的判斷,那他判斷裏面有些什麼玩意呢?

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

我總以爲寫到這裏就應該差很少了,可是我坐下來,靜靜的思考以後發現,仍是差了點什麼。

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

公平鎖到底公平不公平呢?讓咱們一塊兒跟着丙丙走進ReentrantLock的心裏世界。

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

我先畫個圖,幫助你們瞭解下細節:

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

他也是ReentrantLock加鎖釋放鎖的核心,大體的內容我以前一期提到了,我就不過多贅述了,他們看看一次加鎖的過程吧。

A線程準備進去獲取鎖,首先判斷了一下state狀態,發現是0,因此能夠CAS成功,而且修改了當前持有鎖的線程爲本身。

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

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

這個時候有個帶綠帽子的仔C過來了,發現state怎麼是0啊,果斷CAS修改成1,還修改了當前持有鎖的線程爲本身。

B線程被A叫醒準備去獲取鎖,發現state竟然是1,CAS就失敗了,只能失落的繼續回去等待隊列,路線還不忘罵A渣男,怎麼騙本身,欺騙個人感情。

諾以上就是一個非公平鎖的線程,這樣的狀況就有可能像B這樣的線程長時間沒法獲得資源,優勢就是可能有的線程減小了等待時間,提升了利用率。

如今都是默認非公平了,想要公平就得給構造器傳值true。

ReentrantLock lock = new ReentrantLock(true);
複製代碼

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

線A如今想要得到鎖,先去判斷下state,發現也是0,去看了看隊列,本身竟然是第一位,果斷修改了持有線程爲本身。

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

未命名文件 (https://tva1.sinaimg.cn/large/00831rSTly1gcxaojuen2j30oa0jxgmh.jpg)
未命名文件 (https://tva1.sinaimg.cn/large/00831rSTly1gcxaojuen2j30oa0jxgmh.jpg)

線程A暖男來了,持有沒多久就釋放了,改掉了全部的狀態就去喚醒線程B了,這個時候線程C進來了,可是他先判斷了下state發現是0,覺得有戲,而後去看了看隊列,發現前面有人了,做爲新時代的良好市民,果斷排隊去了。

線程B獲得A的召喚,去判斷state了,發現值爲0,本身也是隊列的第一位,那很香呀,能夠獲得了。

總結:

總結我不說話了,可是去獲取鎖判斷的源碼,箭頭所指的位置,如今是否是都被我合理的解釋了,當前線程,state,是不是0,是不是當前線程等等,都去思考下。

鬼知道我爲了畫圖,畫了多少費稿,點個贊過度麼?

課後做業

公平鎖真的公平麼?那什麼層面不是絕對的公平,什麼層面才能算公平?

我是敖丙,一個在互聯網苟且偷生的工具人。

最好的關係是互相成就,各位的「三連」就是丙丙創做的最大動力,咱們下期見!

文章持續更新,能夠微信搜索「 三太子敖丙 」第一時間閱讀,回覆【資料】【面試】【簡歷】有我準備的一線大廠面試資料和簡歷模板,本文 GitHub github.com/JavaFamily 已經收錄,有大廠面試完整考點,歡迎Star。

相關文章
相關標籤/搜索