以下內容轉自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()就會被阻塞,這就出現了重入鎖死。
避免重入鎖死有兩個選擇:
- 編寫程式碼時避免再次獲取已經持有的鎖
- 使用可重入鎖
至於哪個選擇最適合你的專案,得視具體情況而定。可重入鎖通常沒有不可重入鎖那麼好的表現,而且實現起來複雜,但這些情況在你的專案中也許算不上什麼問題。無論你的專案用鎖來實現方便還是不用鎖方便,可重入特性都需要根據具體問題具體分析。