鍍金池/ 教程/ Java/ 重入鎖死
Slipped Conditions
阻塞隊(duì)列
無阻塞算法
嵌套管程鎖死
Java 并發(fā)性和多線程介紹
死鎖
線程安全及不可變性
并發(fā)編程模型
Java 中的讀/寫鎖
剖析同步器
競態(tài)條件與臨界區(qū)
多線程的優(yōu)點(diǎn)
CAS
線程通信
如何創(chuàng)建并運(yùn)行 java 線程
阿姆達(dá)爾定律
避免死鎖
信號(hào)量
多線程的代價(jià)
饑餓和公平
線程池
重入鎖死
Java 中的鎖
Java 內(nèi)存模型
線程安全與共享資源
Java 同步塊

重入鎖死

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

當(dāng)一個(gè)線程重新獲取鎖,讀寫鎖或其他不可重入的同步器時(shí),就可能發(fā)生重入鎖死。可重入的意思是線程可以重復(fù)獲得它已經(jīng)持有的鎖。Java 的 synchronized 塊是可重入的。因此下面的代碼是沒問題的:

(譯者注:這里提到的鎖都是指的不可重入的鎖實(shí)現(xiàn),并不是 Java 類庫中的 Lock 與 ReadWriteLock 類)

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

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

注意 outer()和 inner()都聲明為 synchronized,這在 Java 中這相當(dāng)于 synchronized(this)塊(譯者注:這里兩個(gè)方法是實(shí)例方法,synchronized 的實(shí)例方法相當(dāng)于在 this 上加鎖,如果是 static 方法,則不然,更多閱讀:哪個(gè)對(duì)象才是鎖?)。如果某個(gè)線程調(diào)用了 outer(),outer()中的 inner()調(diào)用是沒問題的,因?yàn)閮蓚€(gè)方法都是在同一個(gè)管程對(duì)象(即 this)上同步的。如果一個(gè)線程持有某個(gè)管程對(duì)象上的鎖,那么它就有權(quán)訪問所有在該管程對(duì)象上同步的塊。這就叫可重入。若線程已經(jīng)持有鎖,那么它就可以重復(fù)訪問所有使用該鎖的代碼塊。

下面這個(gè)鎖的實(shí)現(xiàn)是不可重入的:

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();
    }
}

如果一個(gè)線程在兩次調(diào)用 lock()間沒有調(diào)用 unlock()方法,那么第二次調(diào)用 lock()就會(huì)被阻塞,這就出現(xiàn)了重入鎖死。

避免重入鎖死有兩個(gè)選擇:

  1. 編寫代碼時(shí)避免再次獲取已經(jīng)持有的鎖
  2. 使用可重入鎖

至于哪個(gè)選擇最適合你的項(xiàng)目,得視具體情況而定。可重入鎖通常沒有不可重入鎖那么好的表現(xiàn),而且實(shí)現(xiàn)起來復(fù)雜,但這些情況在你的項(xiàng)目中也許算不上什么問題。無論你的項(xiàng)目用鎖來實(shí)現(xiàn)方便還是不用鎖方便,可重入特性都需要根據(jù)具體問題具體分析。

上一篇:饑餓和公平下一篇:無阻塞算法