<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                ## 34 只求問倒:連環相扣系列鎖面試題 ## 引導語 面試中,問鎖主要是兩方面:鎖的日常使用場景 + 鎖原理,鎖的日常使用場景主要考察對鎖 API 的使用熟練度,看看你是否真的使用過這些 API,而不是紙上談兵,鎖原理主要就是問 AQS 底層的源碼原理了,如果問得更加深入的話,可能會現場讓你實現一個簡單的鎖,簡單要求的會讓你直接使用 AQS API,復雜要求的可能需要重新實現 AQS。 接下來我們一起看一看關于鎖的常見源碼面試題。 ### 1 AQS 相關面試題 #### 1.1 說說自己對 AQS 的理解? 答:回答這樣的問題的時候,面試官主要考察的是你對 AQS 的知識有沒有系統的整理,建議回答的方向是由大到小,由全到細,由使用到原理。 如果和面試官面對面的話,可以邊說邊畫出我們在 AQS 源碼解析上中畫出的整體架構圖,并且可以這么說: 1. AQS 是一個鎖框架,它定義了鎖的實現機制,并開放出擴展的地方,讓子類去實現,比如我們在 lock 的時候,AQS 開放出 state 字段,讓子類可以根據 state 字段來決定是否能夠獲得鎖,對于獲取不到鎖的線程 AQS 會自動進行管理,無需子類鎖關心,這就是 lock 時鎖的內部機制,封裝的很好,又暴露出子類鎖需要擴展的地方; 2. AQS 底層是由同步隊列 + 條件隊列聯手組成,同步隊列管理著獲取不到鎖的線程的排隊和釋放,條件隊列是在一定場景下,對同步隊列的補充,比如獲得鎖的線程從空隊列中拿數據,肯定是拿不到數據的,這時候條件隊列就會管理該線程,使該線程阻塞; 3. AQS 圍繞兩個隊列,提供了四大場景,分別是:獲得鎖、釋放鎖、條件隊列的阻塞,條件隊列的喚醒,分別對應著 AQS 架構圖中的四種顏色的線的走向。 以上三點都是 AQS 全局方面的描述,接著你可以問問面試官要不要說細一點,可以的話,按照 AQS 源碼解析上下兩篇,把四大場景都說一下就好了。 這樣說的好處是很多的: 1. 面試的主動權把握在自己手里,而且都是自己掌握的知識點; 2. 由全到細的把 AQS 全部說完,會給面試官一種你對 AQS 了如指掌的感覺,再加上全部說完耗時會很久,面試時間又很有限,面試官就不會再問關于 AQS 一些刁鉆的問題了,這樣 AQS 就可以輕松過關。 當然如果你對 AQS 了解的不是很深,那么就大概回答下 AQS 的大體架構就好了,就不要說的特別細,免得給自己挖坑。 #### 1.2 多個線程通過鎖請求共享資源,獲取不到鎖的線程怎么辦? 答:加鎖(排它鎖)主要分為以下四步: 1. 嘗試獲得鎖,獲得鎖了直接返回,獲取不到鎖的走到 2; 2. 用 Node 封裝當前線程,追加到同步隊列的隊尾,追加到隊尾時,又有兩步,如 3 和 4; 3. 自旋 + CAS 保證前一個節點的狀態置為 signal; 4. 阻塞自己,使當前線程進入等待狀態。 獲取不到鎖的線程會進行 2、3、4 步,最終會陷入等待狀態,這個描述的是排它鎖。 #### 1.3 問題 1.2 中,排它鎖和共享鎖的處理機制是一樣的么? 答:排它鎖和共享鎖在問題 1.2 中的 2、3、4 步驟都是一樣的, 不同的是在于第一步,線程獲得排它鎖的時候,僅僅把自己設置為同步隊列的頭節點即可,但如果是共享鎖的話,還會去喚醒自己的后續節點,一起來獲得該鎖。 #### 1.4 共享鎖和排它鎖的區別? 答:排它鎖的意思是同一時刻,只能有一個線程可以獲得鎖,也只能有一個線程可以釋放鎖。 共享鎖可以允許多個線程獲得同一個鎖,并且可以設置獲取鎖的線程數量,共享鎖之所以能夠做到這些,是因為線程一旦獲得共享鎖,把自己設置成同步隊列的頭節點后,會自動的去釋放頭節點后等待獲取共享鎖的節點,讓這些等待節點也一起來獲得共享鎖,而排它鎖就不會這么干。 #### 1.5 排它鎖和共享鎖說的是加鎖時的策略,那么鎖釋放時有排它鎖和共享鎖的策略么? 答:是的,排它鎖和共享鎖,主要體現在加鎖時,多個線程能否獲得同一個鎖。 但在鎖釋放時,是沒有排它鎖和共享鎖的概念和策略的,概念僅僅針對鎖獲取。 #### 1.6 描述下同步隊列? 答:同步隊列底層的數據結構就是雙向的鏈表,節點叫做 Node,頭節點叫做 head,尾節點叫做 tail,節點和節點間的前后指向分別叫做 prev、next,如果是面對面面試的話,可以畫一下 AQS 整體架構圖中的同步隊列。 同步隊列的作用:阻塞獲取不到鎖的線程,并在適當時機釋放這些線程。 實現的大致過程:當多個線程都來請求鎖時,某一時刻有且只有一個線程能夠獲得鎖(排它鎖),那么剩余獲取不到鎖的線程,都會到同步隊列中去排隊并阻塞自己,當有線程主動釋放鎖時,就會從同步隊列中頭節點開始釋放一個排隊的線程,讓線程重新去競爭鎖。 #### 1.7 描述下線程入、出同步隊列的時機和過程? 答:(排它鎖為例)從 AQS 整體架構圖中,可以看出同步隊列入隊和出隊都是有兩個箭頭指向,所以入隊和出隊的時機各有兩個。 同步隊列入隊時機: 1. 多個線程請求鎖,獲取不到鎖的線程需要到同步隊列中排隊阻塞; 2. 條件隊列中的節點被喚醒,會從條件隊列中轉移到同步隊列中來。 同步隊列出隊時機: 1. 鎖釋放時,頭節點出隊; 2. 獲得鎖的線程,進入條件隊列時,會釋放鎖,同步隊列頭節點開始競爭鎖。 四個時機的過程可以參考 AQS 源碼解析,1 參考 acquire 方法執行過程,2 參考 signal 方法,3 參考 release 方法,4 參考 await 方法。 #### 1.8 為什么 AQS 有了同步隊列之后,還需要條件隊列? 答:的確,一般情況下,我們只需要有同步隊列就好了,但在上鎖后,需要操作隊列的場景下,一個同步隊列就搞不定了,需要條件隊列進行功能補充,比如當隊列滿時,執行 put 操作的線程會進入條件隊列等待,當隊列空時,執行 take 操作的線程也會進入條件隊列中等待,從一定程度上來看,條件隊列是對同步隊列的場景功能補充。 #### 1.9 描述一下條件隊列中的元素入隊和出隊的時機和過程? 答:入隊時機:執行 await 方法時,當前線程會釋放鎖,并進入到條件隊列。 出隊時機:執行 signal、signalAll 方法時,節點會從條件隊列中轉移到同步隊列中。 具體的執行過程,可以參考源碼解析中 await 和 signal 方法。 #### 1.10 描述一下條件隊列中的節點轉移到同步隊列中去的時機和過程? 答:時機:當有線程執行 signal、signalAll 方法時,從條件隊列的頭節點開始,轉移到同步隊列中去。 過程主要是以下幾步: 1. 找到條件隊列的頭節點,頭節點 next 屬性置為 null,從條件隊列中移除了; 2. 頭節點追加到同步隊列的隊尾; 3. 頭節點狀態(waitStatus)從 CONDITION 修改成 0(初始化狀態); 4. 將節點的前一個節點狀態置為 SIGNAL。 #### 1.11 線程入條件隊列時,為什么需要釋放持有的鎖? 答:原因很簡單,如果當前線程不釋放鎖,一旦跑去條件隊里中阻塞了,后續所有的線程都無法獲得鎖,正確的場景應該是:當前線程釋放鎖,到條件隊列中去阻塞后,其他線程仍然可以獲得當前鎖。 ### 2 AQS 子類鎖面試題 #### 2.1 你在工作中如何使用鎖的,寫一個看一看? 答:這個照實說就好了,具體 demo 可以參考:demo.sixth.ConditionDemo。 #### 2.1 如果我要自定義鎖,大概的實現思路是什么樣子的? 答:現在有很多類似的問題,比如讓你自定義隊列,自定義鎖等等,面試官其實并不是想讓我們重新造一個輪子,而是想考察一下我們對于隊列、鎖理解的深度,我們只需要選擇自己最熟悉的 API 描述一下就好了,所以這題我們可以選擇 ReentrantLock 來描述一下實現思路: 1. 新建內部類繼承 AQS,并實現 AQS 的 tryAcquire 和 tryRelease 兩個方法,在 tryAcquire 方法里面實現控制能否獲取鎖,比如當同步器狀態 state 是 0 時,即可獲得鎖,在 tryRelease 方法里面控制能否釋放鎖,比如將同步器狀態遞減到 0 時,即可釋放鎖; 2. 對外提供 lock、release 兩個方法,lock 表示獲得鎖的方法,底層調用 AQS 的 acquire 方法,release 表示釋放鎖的方法,底層調用 AQS 的 release 方法。 #### 2.2 描述 ReentrantLock 兩大特性:可重入性和公平性?底層分別如何實現的? 答:可重入性說的是線程可以對共享資源重復加鎖,對應的,釋放時也可以重復釋放,對于 ReentrantLock 來說,在獲得鎖的時候,state 會加 1,重復獲得鎖時,不斷的對 state 進行遞增即可,比如目前 state 是 4,表示線程已經對共享資源加鎖了 4 次,線程每次釋放共享資源的鎖時,state 就會遞減 1,直到遞減到 0 時,才算真正釋放掉共享資源。 公平性和非公平指的是同步隊列中的線程得到鎖的機制,如果同步隊列中的線程按照阻塞的順序得到鎖,我們稱之為公平的,反之是非公平的,公平的底層實現是 ReentrantLock 的 tryAcquire 方法(調用的是 AQS 的 hasQueuedPredecessors 方法)里面實現的,要釋放同步隊列的節點時(或者獲得鎖時),判斷當前線程節點是不是同步隊列的頭節點的后一個節點,如果是就釋放,不是則不能釋放,通過這種機制,保證同步隊列中的線程得到鎖時,是按照從頭到尾的順序的。 #### 2.3 如果一個線程需要等待一組線程全部執行完之后再繼續執行,有什么好的辦法么?是如何實現的? 答:CountDownLatch 就提供了這樣的機制,比如一組線程有 5 個,只需要在初始化 CountDownLatch 時,給同步器的 state 賦值為 5,主線程執行 CountDownLatch.await ,子線程都執行 CountDownLatch.countDown 即可。 #### 2.4 Atomic 原子操作類可以保證線程安全,如果操作的對象是自定義的類的話,要如何做呢? 答: Java 為這種情況提供了一個 API:AtomicReference,AtomicReference 類可操作的對象是個泛型,所以支持自定義類。 ### 3 總結 關于 AQS 和鎖場景的面試題,其實網上也很多,各個大廠出的題目也都不一樣,但考察問題的本質都是一致的,如果把 AQS 架構圖中,AQS 的組成和四種顏色箭頭的發起時機,調用過程都弄清楚了,回答 AQS 的各種問題都會游刃有余。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看