1. 程式人生 > >BAT美團滴滴java面試大綱(帶答案版)之四:多線程Lock

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

代碼 res zed 線程數 最好 就是 視頻教程 復制 new

每天學習一點點 編程PDF電子書、視頻教程免費下載:
http://www.shitanlife.com/code

這是多線程的第二篇。

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

  在多線程編程中要渡的第二個“劫”,則是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,多路通知機制  

  

  1. 在Synchronized鎖中,提供了wait、notify、notifyAll等方法,實現了等待/通知模式。那麽在lock中,由Condition配合,也實現了類似的模式。
  2. 其實現實質是,一個Condition包含一個等待隊列,定義多個Condition,那就有多個等待隊列,和上文提到的同步隊列配合使用。同步隊列-等待隊列模型請參考下圖:技術分享圖片
  3. 在上述模型中,調用await方法,相當於把同步隊列首節點(持有鎖的線程),移動到等待隊列。調用signal方法喚醒阻塞的線程,則是將對應Condition等待隊列裏的首節點(等待時間最長),移入同步隊列。
  4. 還有一點需要補充,就是線程的喚醒,調用signal可以正常喚醒;在其他線程中終止線程,也一樣會喚醒,只不過喚醒後,只是拋出InterruptException異常。

每天學習一點點 編程PDF電子書、視頻教程免費下載:
http://www.shitanlife.com/code

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