23、Java併發性和多執行緒-重入鎖死

weixin_34391854發表於2017-06-17

以下內容轉自http://ifeve.com/reentrance-lockout/

重入鎖死與死鎖和巢狀管程鎖死非常相似。鎖和讀寫鎖兩篇文章中都有涉及到重入鎖死的問題。

當一個執行緒重新獲取鎖,讀寫鎖或其他不可重入的同步器時,就可能發生重入鎖死。可重入的意思是執行緒可以重複獲得它已經持有的鎖。Java的synchronized塊是可重入的。因此下面的程式碼是沒問題的:

(譯者注:這裡提到的鎖都是指的不可重入的鎖實現,並不是Java類庫中的Lock與ReadWriteLock類)

public class Reentrant{
    public synchronized outer(){
        inner();
    }

    public synchronized inner(){
        //do something
    }
}

注意outer()和inner()都宣告為synchronized,這在Java中這相當於synchronized(this)塊(譯者注:這裡兩個方法是例項方法,synchronized的例項方法相當於在this上加鎖,如果是static方法,則不然,更多閱讀:哪個物件才是鎖?)。如果某個執行緒呼叫了outer(),outer()中的inner()呼叫是沒問題的,因為兩個方法都是在同一個管程物件(即this)上同步的。如果一個執行緒持有某個管程物件上的鎖,那麼它就有權訪問所有在該管程物件上同步的塊。這就叫可重入。若執行緒已經持有鎖,那麼它就可以重複訪問所有使用該鎖的程式碼塊。

下面這個鎖的實現是不可重入的:

public class Lock{
    private boolean isLocked = false;
    public synchronized void lock() throws InterruptedException{
        while(isLocked){
            wait();
        }
        isLocked = true;
    }

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

如果一個執行緒在兩次呼叫lock()間沒有呼叫unlock()方法,那麼第二次呼叫lock()就會被阻塞,這就出現了重入鎖死。

避免重入鎖死有兩個選擇:

  1. 編寫程式碼時避免再次獲取已經持有的鎖
  2. 使用可重入鎖

至於哪個選擇最適合你的專案,得視具體情況而定。可重入鎖通常沒有不可重入鎖那麼好的表現,而且實現起來複雜,但這些情況在你的專案中也許算不上什麼問題。無論你的專案用鎖來實現方便還是不用鎖方便,可重入特性都需要根據具體問題具體分析。

相關文章