AQS底層原理連環(huán)相扣系列鎖面試題分析

引導(dǎo)語
面試中,問鎖主要是兩方面:鎖的日常使用場景 + 鎖原理,鎖的日常使用場景主要考察對鎖 API 的使用熟練度,看看你是否真的使用過這些 API,而不是紙上談兵,鎖原理主要就是問 AQS 底層的源碼原理了,如果問得更加深入的話,可能會現(xiàn)場讓你實現(xiàn)一個簡單的鎖,簡單要求的會讓你直接使用 AQS API,復(fù)雜要求的可能需要重新實現(xiàn) AQS。
1、AQS 相關(guān)面試題
1.1、說說自己對 AQS 的理解?
答:回答這樣的問題的時候,面試官主要考察的是你對 AQS 的知識有沒有系統(tǒng)的整理,建議回答的方向是由大到小,由全到細,由使用到原理。
如果和面試官面對面的話,可以邊說邊畫出我們在 AQS 源碼解析上中畫出的整體架構(gòu)圖,并且可以這么說:
AQS 是一個鎖框架,它定義了鎖的實現(xiàn)機制,并開放出擴展的地方,讓子類去實現(xiàn),比如我們在 lock 的時候,AQS 開放出 state 字段,讓子類可以根據(jù) state 字段來決定是否能夠獲得鎖,對于獲取不到鎖的線程 AQS 會自動進行管理,無需子類鎖關(guān)心,這就是 lock 時鎖的內(nèi)部機制,封裝的很好,又暴露出子類鎖需要擴展的地方;
AQS 底層是由同步隊列 + 條件隊列聯(lián)手組成,同步隊列管理著獲取不到鎖的線程的排隊和釋放,條件隊列是在一定場景下,對同步隊列的補充,比如獲得鎖的線程從空隊列中拿數(shù)據(jù),肯定是拿不到數(shù)據(jù)的,這時候條件隊列就會管理該線程,使該線程阻塞;
AQS 圍繞兩個隊列,提供了四大場景,分別是:獲得鎖、釋放鎖、條件隊列的阻塞,條件隊列的喚醒,分別對應(yīng)著 AQS 架構(gòu)圖中的四種顏色的線的走向。
以上三點都是 AQS 全局方面的描述,接著你可以問問面試官要不要說細一點,可以的話,按照 AQS 源碼解析上下兩篇,把四大場景都說一下就好了。
這樣說的好處是很多的:
面試的主動權(quán)把握在自己手里,而且都是自己掌握的知識點;
由全到細的把 AQS 全部說完,會給面試官一種你對 AQS 了如指掌的感覺,再加上全部說完耗時會很久,面試時間又很有限,面試官就不會再問關(guān)于 AQS 一些刁鉆的問題了,這樣 AQS 就可以輕松過關(guān)。
當(dāng)然如果你對 AQS 了解的不是很深,那么就大概回答下 AQS 的大體架構(gòu)就好了,就不要說的特別細,免得給自己挖坑。
1.2、多個線程通過鎖請求共享資源,獲取不到鎖的線程怎么辦?
答:加鎖(排它鎖)主要分為以下四步:
嘗試獲得鎖,獲得鎖了直接返回,獲取不到鎖的走到 2;
用 Node 封裝當(dāng)前線程,追加到同步隊列的隊尾,追加到隊尾時,又有兩步,如 3 和 4;
自旋 + CAS 保證前一個節(jié)點的狀態(tài)置為 signal;
阻塞自己,使當(dāng)前線程進入等待狀態(tài)。
獲取不到鎖的線程會進行 2、3、4 步,最終會陷入等待狀態(tài),這個描述的是排它鎖。
1.3、問題 1.2 中,排它鎖和共享鎖的處理機制是一樣的么?
答:排它鎖和共享鎖在問題 1.2 中的 2、3、4 步驟都是一樣的, 不同的是在于第一步,線程獲得排它鎖的時候,僅僅把自己設(shè)置為同步隊列的頭節(jié)點即可,但如果是共享鎖的話,還會去喚醒自己的后續(xù)節(jié)點,一起來獲得該鎖。
1.4、共享鎖和排它鎖的區(qū)別?
答:排它鎖的意思是同一時刻,只能有一個線程可以獲得鎖,也只能有一個線程可以釋放鎖。
共享鎖可以允許多個線程獲得同一個鎖,并且可以設(shè)置獲取鎖的線程數(shù)量,共享鎖之所以能夠做到這些,是因為線程一旦獲得共享鎖,把自己設(shè)置成同步隊列的頭節(jié)點后,會自動的去釋放頭節(jié)點后等待獲取共享鎖的節(jié)點,讓這些等待節(jié)點也一起來獲得共享鎖,而排它鎖就不會這么干。
1.5、排它鎖和共享鎖說的是加鎖時的策略,那么鎖釋放時有排它鎖和共享鎖的策略么?
答:是的,排它鎖和共享鎖,主要體現(xiàn)在加鎖時,多個線程能否獲得同一個鎖。
但在鎖釋放時,是沒有排它鎖和共享鎖的概念和策略的,概念僅僅針對鎖獲取。
1.6、描述下同步隊列?
答:同步隊列底層的數(shù)據(jù)結(jié)構(gòu)就是雙向的鏈表,節(jié)點叫做 Node,頭節(jié)點叫做 head,尾節(jié)點叫做 tail,節(jié)點和節(jié)點間的前后指向分別叫做 prev、next,如果是面對面面試的話,可以畫一下 AQS 整體架構(gòu)圖中的同步隊列。
同步隊列的作用:阻塞獲取不到鎖的線程,并在適當(dāng)時機釋放這些線程。
實現(xiàn)的大致過程:當(dāng)多個線程都來請求鎖時,某一時刻有且只有一個線程能夠獲得鎖(排它鎖),那么剩余獲取不到鎖的線程,都會到同步隊列中去排隊并阻塞自己,當(dāng)有線程主動釋放鎖時,就會從同步隊列中頭節(jié)點開始釋放一個排隊的線程,讓線程重新去競爭鎖。
1.7、描述下線程入、出同步隊列的時機和過程?
答:(排它鎖為例)從 AQS 整體架構(gòu)圖中,可以看出同步隊列入隊和出隊都是有兩個箭頭指向,所以入隊和出隊的時機各有兩個。
同步隊列入隊時機:
多個線程請求鎖,獲取不到鎖的線程需要到同步隊列中排隊阻塞;
條件隊列中的節(jié)點被喚醒,會從條件隊列中轉(zhuǎn)移到同步隊列中來。
同步隊列出隊時機:
鎖釋放時,頭節(jié)點出隊;
獲得鎖的線程,進入條件隊列時,會釋放鎖,同步隊列頭節(jié)點開始競爭鎖。
四個時機的過程可以參考 AQS 源碼解析,1 參考 acquire 方法執(zhí)行過程,2 參考 signal 方法,3 參考 release 方法,4 參考 await 方法。
1.8、為什么 AQS 有了同步隊列之后,還需要條件隊列?
答:的確,一般情況下,我們只需要有同步隊列就好了,但在上鎖后,需要操作隊列的場景下,一個同步隊列就搞不定了,需要條件隊列進行功能補充,比如當(dāng)隊列滿時,執(zhí)行 put 操作的線程會進入條件隊列等待,當(dāng)隊列空時,執(zhí)行 take 操作的線程也會進入條件隊列中等待,從一定程度上來看,條件隊列是對同步隊列的場景功能補充。
1.9、描述一下條件隊列中的元素入隊和出隊的時機和過程?
答:
入隊時機:執(zhí)行 await 方法時,當(dāng)前線程會釋放鎖,并進入到條件隊列。
出隊時機:執(zhí)行 signal、signalAll 方法時,節(jié)點會從條件隊列中轉(zhuǎn)移到同步隊列中。
具體的執(zhí)行過程,可以參考源碼解析中 await 和 signal 方法。
1.10、描述一下條件隊列中的節(jié)點轉(zhuǎn)移到同步隊列中去的時機和過程?
答:時機:當(dāng)有線程執(zhí)行 signal、signalAll 方法時,從條件隊列的頭節(jié)點開始,轉(zhuǎn)移到同步隊列中去。
過程主要是以下幾步:
找到條件隊列的頭節(jié)點,頭節(jié)點 next 屬性置為 null,從條件隊列中移除了;
頭節(jié)點追加到同步隊列的隊尾;
頭節(jié)點狀態(tài)(waitStatus)從 CONDITION 修改成 0(初始化狀態(tài));
將節(jié)點的前一個節(jié)點狀態(tài)置為 SIGNAL。
1.11、線程入條件隊列時,為什么需要釋放持有的鎖?
答:原因很簡單,如果當(dāng)前線程不釋放鎖,一旦跑去條件隊里中阻塞了,后續(xù)所有的線程都無法獲得鎖,正確的場景應(yīng)該是:當(dāng)前線程釋放鎖,到條件隊列中去阻塞后,其他線程仍然可以獲得當(dāng)前鎖。
2、AQS 子類鎖面試題
2.1、你在工作中如何使用鎖的,寫一個看一看?
答:這個照實說就好了
2.2、如果我要自定義鎖,大概的實現(xiàn)思路是什么樣子的?
答:現(xiàn)在有很多類似的問題,比如讓你自定義隊列,自定義鎖等等,面試官其實并不是想讓我們重新造一個輪子,而是想考察一下我們對于隊列、鎖理解的深度,我們只需要選擇自己最熟悉的 API 描述一下就好了,所以這題我們可以選擇 ReentrantLock 來描述一下實現(xiàn)思路:
新建內(nèi)部類繼承 AQS,并實現(xiàn) AQS 的 tryAcquire 和 tryRelease 兩個方法,在 tryAcquire 方法里面實現(xiàn)控制能否獲取鎖,比如當(dāng)同步器狀態(tài) state 是 0 時,即可獲得鎖,在 tryRelease 方法里面控制能否釋放鎖,比如將同步器狀態(tài)遞減到 0 時,即可釋放鎖;
對外提供 lock、release 兩個方法,lock 表示獲得鎖的方法,底層調(diào)用 AQS 的 acquire 方法,release 表示釋放鎖的方法,底層調(diào)用 AQS 的 release 方法。
2.3、描述 ReentrantLock 兩大特性:可重入性和公平性?底層分別如何實現(xiàn)的?
答:可重入性說的是線程可以對共享資源重復(fù)加鎖,對應(yīng)的,釋放時也可以重復(fù)釋放,對于 ReentrantLock 來說,在獲得鎖的時候,state 會加 1,重復(fù)獲得鎖時,不斷的對 state 進行遞增即可,比如目前 state 是 4,表示線程已經(jīng)對共享資源加鎖了 4 次,線程每次釋放共享資源的鎖時,state 就會遞減 1,直到遞減到 0 時,才算真正釋放掉共享資源。
公平性和非公平指的是同步隊列中的線程得到鎖的機制,如果同步隊列中的線程按照阻塞的順序得到鎖,我們稱之為公平的,反之是非公平的,公平的底層實現(xiàn)是 ReentrantLock 的 tryAcquire 方法(調(diào)用的是 AQS 的 hasQueuedPredecessors 方法)里面實現(xiàn)的,要釋放同步隊列的節(jié)點時(或者獲得鎖時),判斷當(dāng)前線程節(jié)點是不是同步隊列的頭節(jié)點的后一個節(jié)點,如果是就釋放,不是則不能釋放,通過這種機制,保證同步隊列中的線程得到鎖時,是按照從頭到尾的順序的。
2.4、如果一個線程需要等待一組線程全部執(zhí)行完之后再繼續(xù)執(zhí)行,有什么好的辦法么?是如何實現(xiàn)的?
答:CountDownLatch 就提供了這樣的機制,比如一組線程有 5 個,只需要在初始化 CountDownLatch 時,給同步器的 state 賦值為 5,主線程執(zhí)行 CountDownLatch.await ,子線程都執(zhí)行 CountDownLatch.countDown 即可。
2.5、Atomic 原子操作類可以保證線程安全,如果操作的對象是自定義的類的話,要如何做呢?
答: Java 為這種情況提供了一個 API:AtomicReference,AtomicReference 類可操作的對象是個泛型,所以支持自定義類。
3、總結(jié)
關(guān)于 AQS 和鎖場景的面試題,其實網(wǎng)上也很多,各個大廠出的題目也都不一樣,但考察問題的本質(zhì)都是一致的,如果把 AQS 架構(gòu)圖中,AQS 的組成和四種顏色箭頭的發(fā)起時機,調(diào)用過程都弄清楚了,回答 AQS 的各種問題都會游刃有余。
以上就是AQS底層原理連環(huán)相扣系列鎖面試題分析的詳細內(nèi)容,更多關(guān)于AQS原理連環(huán)相扣系列鎖面試分析的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Java面試通關(guān)要點匯總(備戰(zhàn)秋招)
這篇文章主要介紹了Java面試通關(guān)要點匯總(備戰(zhàn)秋招),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2020-09-08- 這篇文章主要介紹了2020Java面試題最新(五鎖機制篇),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2020-02-12
- 這篇文章主要介紹了史上最強多線程面試44題和答案:線程鎖+線程池+線程同步等,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-08-06
Java研發(fā)京東4面:事務(wù)隔離+樂觀鎖+HashMap+秒殺設(shè)計+微服務(wù)
這篇文章主要介紹了Java研發(fā)京東4面:事務(wù)隔離+樂觀鎖+HashMap+秒殺設(shè)計+微服務(wù),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-07-24Java研發(fā)面試99題(含答案):JVM+Spring+MySQL+線程池+鎖
這篇文章主要介紹了Java研發(fā)面試99題,主要包括了JVM,Spring,MySQL,線程池,鎖等,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2019-07-16