王者併發課-鉑金3:一勞永逸-如何理解鎖的多次可重入問題

秦二爺發表於2022-06-13

歡迎來到《王者併發課》,本文是該系列文章中的第16篇

在前面的文章《鉑金1:探本溯源-為何說Lock介面是Java中鎖的基礎》中,我們提到了鎖的可重入問題,並作了簡單介紹。鑑於鎖的可重入是一個重要概念,所以本文把拿出來做一次單獨講解,以幫助你徹底理解它。

一、鎖的可重入所造成的問題

首先,我們通過一段示例程式碼看鎖的可重入是如何導致問題發生,以理解它的重要性。

public class ReentrantWildArea {
    // 野區鎖定
    private boolean isAreaLocked = false;

    // 進入野區A
    public synchronized void enterAreaA() throws InterruptedException {
        isAreaLocked = true;
        System.out.println("已經進入野區A...");
        enterAreaB();
    }
    // 進入野區B
    public synchronized void enterAreaB() throws InterruptedException {
        while (isAreaLocked) {
            System.out.println("野區B方法進入等待中...");
            wait();
        }
        System.out.println("已經進入野區B...");
    }

    public synchronized void unlock() {
        isAreaLocked = false;
        notify();
    }
}

在上面這段程式碼中,我們建立了一片野區,包含了野區A野區B。接著,我們再建立一個打野英雄鎧,讓他進去野區打野,看看會發生什麼事情。

public static void main(String[] args) {
  // 打野英雄鎧進入野區
  Thread kaiThread = new Thread(() -> {
    ReentrantWildArea wildArea = new ReentrantWildArea();
    try {
      wildArea.enterAreaA();
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  });
  kaiThread.start();
}

輸出結果如下:

已經進入野區A...
野區B方法進入等待中...

從結果中可以看到,雖然在同一塊野區,但是鎧只進入野區A,卻沒能進入野區B,被阻塞在半道上了。從程式碼分析上看,野區的兩個方法都宣告瞭synchronized,但鎧在進入野區A之後,野區進行了鎖定isAreaLocked = true,導致鎧進入野區B時失敗。

這就是典型的鎖的可重入所造成的問題。在併發程式設計時,如果未能處理好這一問題,將會造成執行緒的無限阻塞,其後果和死鎖相當

二、理解鎖的可重入

所謂鎖的可重入,指的是鎖可以被執行緒 重複遞迴 呼叫,也可以理解為對同一把鎖的重複獲取。 如果未能處理好鎖的可重入問題,將會導致和死鎖類似的問題。

三、如何避免鎖的可重入問題

避免鎖的可重入問題,需要注意兩個方面:

  • 儘量避免編寫需要重入獲取鎖的程式碼
  • 如果需要,使用可重入鎖

在Java中,synchronized是可以重入的,下面的這段程式碼在呼叫時不會產生重入問題。

public class WildMonster {
    public synchronized void A() {
        B();
    }
    
    public synchronized void B() {
       doSomething...
    }
}

但是,基於Lock介面所實現的各種鎖並不總是支援可重入的。在前面的文章中,我們已經展示過不支援重入的Lock介面實現。在具體的場景中使用時,需要務必注意這點。如果需要可重入鎖,可以使用Java中的ReentrantLock類。

小結

在本文中,我們再次介紹了鎖的可重入問題,並介紹了其產生的原因及避免方式。Java中的synchronized關鍵字支援鎖的可重入,但是其他顯示鎖並非總是支援這一特性,在使用時需要注意。

此外,需要注意的是,鎖的可重入對鎖的效能有一定的影響,而且實現起來更為複雜。所以,我們不能說鎖的可重入與不可重入哪個好,這要取決於具體的問題

正文到此結束,恭喜你又上了一顆星✨

夫子的試煉

  • 檢視ReentrantLock原始碼,瞭解其支援可重入的原理。

延伸閱讀與參考資料

關於作者

從業近十年,先後從事敏捷與DevOps諮詢、Tech Leader和管理等工作,對分散式高併發架構有豐富的實戰經驗。熱衷於技術分享和特定領域書籍翻譯,掘金小冊《高併發秒殺的設計精要與實現》作者。


關注公眾號【MetaThoughts】,及時獲取文章更新和文稿。

如果本文對你有幫助,歡迎點贊關注監督,我們一起從青銅到王者

相關文章