動態高併發時爲何推薦ReentrantLock而不是Synchronized?

前言碎語

    SynchronizedReentrantLock 你們應該都不陌生了,做爲java中最經常使用的本地鎖,最第一版本中 ReentrantLock 的性能是遠遠強於 Synchronized 的,後續java在一次次的版本迭代中 對 Synchronized 進行了大量的優化,直到 jdk1.6 以後,兩種鎖的性能已經相差無幾,甚至 Synchronized 的自動釋放鎖會更好用。java

   在面試時被問到 Synchronized 和 ReentrantLock 的使用選擇時,不少朋友都脫口而出的說用 Synchronized ,甚至在我面試的時候問面試者,也不多有人可以答出因此然來,moon 想說,這可不必定,只對標題感興趣的同窗能夠直接劃到最後,我可不是標題黨~面試

Synchronized使用

   在 java 代碼中 synchronized 的使用是很是簡單的設計模式

  • 1.直接貼在方法上
  • 2.貼在代碼塊兒上

程序運行期間,Synchronized那一起代碼發生麼什麼?

   來看一張圖api

   在多線程運行過程當中,線程會去先搶對象的監視器,這個監視器是對象獨有的,其實就至關於一把鑰匙,搶到了,那你就得到了當前代碼塊兒的執行權。數據結構

   其餘沒有搶到的線程會進入隊列(SynchronizedQueue)當中等待,等待當前線程執行完後,釋放鎖.多線程

   最後當前線程執行完畢後通知出隊而後繼續重複當前過程.併發

   從 jvm 的角度來看 monitorenter 和 monitorexit 指令表明着代碼的執行與結束框架

SynchronizedQueue:

   SynchronizedQueue 是一個比較特殊的隊列,它沒有存儲功能,它的功能就是維護一組線程,其中每一個插入操做必須等待另外一個線程的移除操做,一樣任何一個移除操做都等待另外一個線程的插入操做。所以此隊列內部其 實沒有任何一個元素,或者說容量是0,嚴格說並非一種容器。因爲隊列沒有容量,所以不能調用 peek 操做,由於只有移除元素時纔有元素。jvm

舉個例子:

   喝酒的時候,先把酒倒入酒盅,而後再倒入酒杯,這就是正常的隊列高併發

   喝酒的時候,把酒直接倒入酒杯,這就是 SynchronizedQueue

   這個例子應該很清晰易懂了,它的好處就是能夠直接傳遞,省去了一個第三方傳遞的過程。

聊聊細節,鎖升級的過程

   在 jdk1.6 之前,Synchronized 是一個重量級鎖,仍是先貼一張圖

   這就是爲何說,Synchronized 是一個重量級鎖的緣由,由於每一次鎖的資源都是直接和 cpu 去申請的,而 cpu 的鎖數量是固定的,當 cpu 鎖資源使用完後還會進行鎖等待,這是一個很是耗時的操做。

   可是在jdk1.6,針對代碼層面進行了大量的優化,也就是咱們常說的鎖升級的過程

這就是一個鎖升級的過程,咱們簡單的說說:

  • 無鎖:對象一開始就是無鎖狀態。
  • 偏向鎖:至關於給對象貼了一個標籤(將本身的線程 id 存入對象頭中),下次我再進來時,發現標籤是個人,我就能夠繼續使用了。
  • 自旋鎖:想象一下有一個廁所,裏面有一我的在,你很想上可是隻有一個坑位,因此你只能徘徊等待,等那我的出來之後,你就可使用了。 這個自旋是使用 cas 來保證原子性的,關於 cas 我這裏就再也不贅述了。
  • 重量級鎖:直接向 cpu 去申請申請鎖,其餘的線程都進入隊列中等待。

鎖升級是何時發生的?

  • 偏向鎖:一個線程獲取鎖時會由無鎖升級爲偏向鎖
  • 自旋鎖:當產生線程競爭時由偏向鎖升級爲自旋鎖,想象一下 while(true) ;
  • 重量級鎖:當線程競爭到達必定數量或超過必定時間時,晉升爲重量級鎖

鎖的信息是記錄在哪裏的?

   這張圖是對象頭中 markword 的數據結構,鎖的信息就是在這裏存放的,很清楚的代表了鎖在升級的時候鎖信息的變更,其實就是經過二進制的數值,來對對象進行一個標記,每一個數值表明一種狀態

既然synchronized有鎖升級那麼有鎖降級嗎?

   這個問題和咱們的題目就有很大的關聯了。

   在 HotSpot 虛擬機中是有鎖降級的,可是僅僅只發生在 STW 的時候,只有垃圾回收線程可以觀測到它,也就是說,在咱們正常使用的過程當中是不會發生鎖降級的,只有在 GC 的時候纔會降級。

   因此題目的答案,你懂了嗎?哈哈,咱們接着往下走。

ReentrantLock的使用

   ReentrantLock 的使用也是很是簡單的,與 Synchronized 的不一樣就是須要本身去手動釋放鎖,爲了保證必定釋放,因此一般都是和 try~finally 配合使用的。

ReentrantLock的原理

   ReentrantLock 意爲可重入鎖,提及 ReentrantLock 就不得不說 AQS ,由於其底層就是使用 AQS 去實現的。

   ReentrantLock有兩種模式,一種是公平鎖,一種是非公平鎖。

  • 公平模式下等待線程入隊列後會嚴格按照隊列順序去執行
  • 非公平模式下等待線程入隊列後有可能會出現插隊狀況

   這就是ReentrantLock的結構圖,咱們看這張圖實際上是很簡單的,由於主要的實現都交給AQS去作了,咱們下面着重聊一下AQS。

AQS

   AQS(AbstractQueuedSynchronizer): AQS 能夠理解爲就是一個能夠實現鎖的框架

   簡單的流程理解

   公平鎖:

  • 第一步:獲取狀態的 state 的值。
    • 若是 state=0 即表明鎖沒有被其它線程佔用,執行第二步。
    • 若是 state!=0 則表明鎖正在被其它線程佔用,執行第三步。
  • 第二步:判斷隊列中是否有線程在排隊等待。
    • 若是不存在則直接將鎖的全部者設置成當前線程,且更新狀態 state 。
    • 若是存在就入隊。
  • 第三步:判斷鎖的全部者是否是當前線程。
    • 若是是則更新狀態 state 的值。
    • 若是不是,線程進入隊列排隊等待。

   非公平鎖:

  • 第一步:獲取狀態的 state 的值。

    • 若是 state=0 即表明鎖沒有被其它線程佔用,則設置當前鎖的持有者爲當前線程,該操做用 CAS 完成。
    • 若是不爲0或者設置失敗,表明鎖被佔用進行下一步。
  • 此時獲取 state 的值,

    • 若是是0,表明恰好線程釋放了鎖,此時將鎖的持有者設爲本身
    • 若是不是0,則查看線程持有者是否是本身
      • 若是是,則給state+1,獲取鎖
      • 若是不是,則進入隊列等待

   讀完以上的部分相信你對AQS已經有了一個比較清楚的概念了,因此咱們來聊聊小細節。

   AQS使用state同步狀態(0表明無鎖,1表明有),並暴露出 getState 、 setState 以及 compareAndSet 操做來讀取和更新這個狀態,使得僅當同步狀態擁有一個指望值的時候,纔會被原子地設置成新值。

   當有線程獲取鎖失敗後,AQS是經過一個雙向的同步隊列來完成同步狀態的管理,就被添加到隊列末尾。

   這是定義頭尾節點的代碼,咱們能夠先使用 volatile 去修飾的,就是保證讓其餘線程可見,AQS 實際上就是修改頭尾兩個節點來完成入隊和出隊操做的。

   AQS 在鎖的獲取時,並不必定只有一個線程才能持有這個鎖,因此此時有了獨佔模式和共享模式的區別,咱們本篇文章中的 ReentrantLock 使用的就是獨佔模式,在多線程的狀況下只會有一個線程獲取鎖。

   獨佔模式的流程是比較簡單的,就根據state是否爲0來判斷是否有線程已經得到了鎖,沒有就阻塞,有就繼續執行後續代碼邏輯。

   共享模式的流程根據state是否大於0來判斷是否有線程已經得到了鎖,若是不大於0,就阻塞,若是大於0,經過CAS的原子操做來自減state的值,而後繼續執行後續代碼邏輯。

ReentrantLock和Synchronized的區別

  • 其實ReentrantLock和Synchronized最核心的區別就在於Synchronized適合於併發競爭低的狀況,由於Synchronized的鎖升級若是最終升級爲重量級鎖在使用的過程當中是沒有辦法消除的,意味着每次都要和cpu去請求鎖資源,而ReentrantLock主要是提供了阻塞的能力,經過在高併發下線程的掛起,來減小競爭,提升併發能力,因此咱們文章標題的答案,也就顯而易見了。

  • synchronized是一個關鍵字,是由jvm層面去實現的,而ReentrantLock是由java api去實現的。

  • synchronized是隱式鎖,能夠自動釋放鎖,ReentrantLock是顯式鎖,須要手動釋放鎖

  • ReentrantLock可讓等待鎖的線程響應中斷,而synchronized卻不行,使用synchronized時,等待的線程會一直等待下去,不可以響應中斷。

  • ReentrantLock能夠獲取鎖狀態,而synchronized不能。

說說標題的答案

   其實題目的答案就在上一欄目的第一條,也是核心的區別,synchronized升級爲重量級鎖後沒法在正常狀況下完成降級,而ReentrantLock是經過阻塞來提升性能的,在設計模式上就體現出了對多線程狀況的支持。

相關文章
相關標籤/搜索