Synchronized與ReentrantLock面試知識點總結

面試經常會被問到Java的鎖,主要是Synchronized與以ReentrantLock為代表的鎖的一些原理機制,結合我之前面試的經歷,總結一下回答的關鍵點。想詳細瞭解的可以查詢資料,全面學習一下。如有疏漏和錯誤,請批評指正。

Synchronized

鎖程式碼:編譯後會生成monitorenter和monitorexit指令。

鎖方法:會在常量池裡的metho_info處有一個ACC_SYNCHRONIZED的標識。

物件鎖和類鎖:物件鎖&類鎖。

Synchronized鎖升級(重點):

偏向鎖:物件頭部的markword,改為偏向鎖模式,記錄持有鎖的執行緒ID——執行緒透過CAS寫入。其依據是:大多數情況下,鎖不僅不存在多執行緒競爭,而且總是由同一執行緒獲得。因為記錄了執行緒ID,所以實現了可重入鎖。

輕量鎖:markword改為輕量鎖,並指向棧中鎖記錄的指標,適應執行緒交替執行同步塊的場景。其依據是:絕大部分鎖,在整個同步週期內不存在競爭。

自旋鎖:輕量級鎖時,執行緒搶鎖失敗,透過空迴圈,使用CAS獲取鎖。其依據是:在大多數情況下,執行緒持有鎖的時間都不會太長,自旋幾次就能拿到鎖,避免了執行緒交給OS,從使用者態轉為核心態。後來最佳化為自適應自旋鎖。

重量鎖:多次自旋依然拿不到鎖,交給OS掛起。

ReentrantLock

談ReentrantLock其實主要是談AQS。

關鍵點有兩個:

1、state。是個volatile int。也有long的實現。代表資源的佔用狀態,0為未佔用。如果是獨佔鎖例如ReentrantLock,state=1代表被佔用,加鎖、釋放鎖就是state0,1的轉換。透過持有鎖的currentThread與Thread比較,可以實現可重入鎖。

如果是共享鎖,例如

Semaphore、CountDownLatch、

CycliBarrier,state就不只是0,1。

例如

Semaphore,初始化state=5,加鎖-1,釋放鎖+1,一旦state=0,則後續執行緒就拿不到鎖。

CountDownLatch(

CycliBarrier與之差不多

),初始化state=5,加鎖-1,待state=0時,喚醒要執行的後續執行緒。

2、CLH雙向佇列。如果是非公平鎖,執行緒自旋一次入列,如果是公平鎖,直接入列。鎖釋放之後,非公平鎖根據自旋情況決定哪個執行緒能搶到鎖,公平鎖根據FIFO原則從佇列取執行緒。