BAT美團滴滴java面試大綱(帶答案版)之四:多線程Lock

繼續面試大綱系列文章。java

 

  這是多線程的第二篇。面試

  多線程就像武學中對的吸星大法,理解透了用好了能夠得道成仙,俯瞰芸芸衆生;而濫用則會遭其反噬。編程

  在多線程編程中要渡的第二個「劫」,則是Lock。在不少時候,包括面試、包括實際項目應用,咱們都會拿來和synchronized對比一番。安全

  咱們知道,多線程的核心思想是經過增長線程數量來併發的運行,來提升效率,也就是數量決勝論,而不是質量決勝(提升每一個線程的處理能力)。多線程編程中面臨的最大挑戰,是如何解決多個線程同時修改一個公用的變量所帶來的變量值不肯定性問題。順着這個思路分析,經常使用辦法,無非就是,要麼對變量動手,在一個線程修改時,變量值被鎖定。要麼是對修改的操做動手,在該段代碼執行時,對其加鎖,其餘線程不能夠在同一時刻進入該段代碼執行。多線程

  同synchronized同樣,Lock,也是實現了後一種辦法。只不過,實現方式,有所不一樣。併發

 

Lock

 

  1. 問:你平時涉及到多線程編程多很少?談談你對Lock鎖的理解
  2. 分析:最好對比着synchronized來說
  3. 答:
    1.   在多線程編程中,爲了達到線程安全的目的,咱們每每經過加鎖的方式來實現。Lock鎖是java代碼級別來實現的,相對於synchronizedd在功能性上,有所增強,主要是,公平鎖,輪詢鎖,定時鎖,可中斷鎖等,還增長了多路通知機制(Condition),能夠用一個鎖來管理多個同步塊。另外在使用的時候,必須手動的釋放鎖。
    2. 詳細分析:
      1. Lock鎖的實現,主要是藉助於隊列同步器(咱們經常見到的AQS)來實現。它包括一個int變量來表示狀態;一個FIFO隊列,來存儲獲取資源的排隊線程。
      2. 當一個線程申請資源時,就是是獲取當前的同步狀態,並判斷是否可符合預期,若是是,則經過CAS操做,來修改上述Int變量標識的同步狀態。若是否,則線程進入隊列排隊(這是在通常狀況,在使用tyrLock時,是直接返回獲取鎖失敗)。
          1. 鎖有獨佔鎖和共享鎖。獨佔鎖就是在同一時刻,只容許同一個線程持有該鎖;共享鎖實現的時候和獨佔鎖稍有不一樣,不是簡單的修改同步狀態(好比1和0),而是獲取這個值,當值大於0時,即標識獲取共享鎖成功(隱含意思是每一個線程獲取鎖成功後,這個值減1)。這裏附上獨佔鎖的實現源碼(源碼片斷來自《java併發編程的藝術》,並加上本身的註釋):
            public class Mutex implements Lock {  
              
                // 靜態內部類,自定義同步器  
                private static class Sync extends AbstractQueuedSynchronizer{  
                    // 該方法用於判斷當前鎖是否在獨佔模式下被佔用狀態  
                    protected boolean isHeldExclusively(){  
                        return getState() == 1;  
                    }  
              
                    // 獲取鎖!!! 
                    public boolean tryAcquire(int acquires){ 
                  //典型的CAS原子操做,若是初始狀態爲0,能夠得到鎖
            if (compareAndSetState(0, 1)){ setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } //釋放鎖,將當前狀態設置爲0 protected boolean tryRelease(int releases){ if (getState() == 0){ throw new IllegalMonitorStateException(); } setExclusiveOwnerThread(null); setState(0); return true; } // 返回一個Condition,每一個condition都包含了一個condition隊列 ,這個後續再說 Condition newCondition(){ return new ConditionObject(); } }

             

      3. Lock鎖中,支持可中斷的鎖,實現原理是,隊列中的等待線程,能夠響應其餘線程發起的中斷信號,拋出InterruptdException異常。
      4. 關於同步隊列,須要瞭解,獲取同步狀態失敗的線程,被包裝爲Node節點後,加入隊列尾,這個操做是CAS操做,以保證線程安全,失敗就死循環重試;而隊列首節點,則是當前持有鎖的線程。該節點一旦釋放鎖,會喚醒後繼節點。
      5. 關於喚醒,是這樣的,每一個在同步隊列中的阻塞線程,都處於自旋的狀態,不斷的嘗試獲取鎖。這樣,當首節點釋放鎖喚醒後繼線程後,被喚醒的線程,還須要判斷是否前繼線程是首線程,是則獲取同步狀態(鎖)成功。

  4.擴展:Condition,多路通知機制  ui

  

  1. 在Synchronized鎖中,提供了wait、notify、notifyAll等方法,實現了等待/通知模式。那麼在lock中,由Condition配合,也實現了相似的模式。
  2. 其實現實質是,一個Condition包含一個等待隊列,定義多個Condition,那就有多個等待隊列,和上文提到的同步隊列配合使用。同步隊列-等待隊列模型請參考下圖:
  3. 在上述模型中,調用await方法,至關於把同步隊列首節點(持有鎖的線程),移動到等待隊列。調用signal方法喚醒阻塞的線程,則是將對應Condition等待隊列裏的首節點(等待時間最長),移入同步隊列。
  4. 還有一點須要補充,就是線程的喚醒,調用signal能夠正常喚醒;在其餘線程中終止線程,也同樣會喚醒,只不過喚醒後,只是拋出InterruptException異常。
  5. 若是你看的爽,請點擊右下角的「推薦」,是對小端堅持分享原創的最大鼓勵。也能夠關注小端的我的公衆號 :   pnxsxb  ,會分享更多的原創技術文章。

歡迎掃描如下二維碼關注公衆號:小端有話說:spa

相關文章
相關標籤/搜索