鍍金池/ 教程/ Java/ 死鎖
并發(fā)新特性—信號量 Semaphore
線程間協(xié)作:wait、notify、notifyAll
notify 通知的遺漏
notifyAll 造成的早期通知問題
多線程的實現(xiàn)方法
深入 Java 內存模型(1)
多線程環(huán)境下安全使用集合 API
并發(fā)新特性—Lock 鎖與條件變量
生產者—消費者模型
深入 Java 內存模型(2)
線程中斷
Volatile 關鍵字(上)
并發(fā)新特性—阻塞隊列與阻塞棧
可重入內置鎖
守護線程與線程阻塞
并發(fā)新特性—障礙器 CyclicBarrier
Volatile 關鍵字(下)
synchronized 關鍵字
synchronized 的另個一重要作用:內存可見性
并發(fā)新特性—Executor 框架與線程池
并發(fā)性與多線程介紹
死鎖
實現(xiàn)內存可見性的兩種方法比較:synchronized 和 Volatile
線程掛起、恢復與終止

死鎖

當線程需要同時持有多個鎖時,有可能產生死鎖??紤]如下情形:

線程 A 當前持有互斥所鎖 lock1,線程 B 當前持有互斥鎖 lock2。接下來,當線程 A 仍然持有 lock1 時,它試圖獲取 lock2,因為線程 B 正持有 lock2,因此線程 A 會阻塞等待線程 B 對 lock2 的釋放。如果此時線程 B 在持有 lock2 的時候,也在試圖獲取 lock1,因為線程 A 正持有 lock1,因此線程 B 會阻塞等待 A 對 lock1 的釋放。二者都在等待對方所持有鎖的釋放,而二者卻又都沒釋放自己所持有的鎖,這時二者便會一直阻塞下去。這種情形稱為死鎖。

下面給出一個兩個線程間產生死鎖的示例,如下:

public class Deadlock extends Object {  
    private String objID;  

    public Deadlock(String id) {  
        objID = id;  
    }  

    public synchronized void checkOther(Deadlock other) {  
        print("entering checkOther()");  
        try { Thread.sleep(2000); }   
        catch ( InterruptedException x ) { }  
        print("in checkOther() - about to " + "invoke 'other.action()'");  

        //調用other對象的action方法,由于該方法是同步方法,因此會試圖獲取other對象的對象鎖  
        other.action();  
        print("leaving checkOther()");  
    }  

    public synchronized void action() {  
        print("entering action()");  
        try { Thread.sleep(500); }   
        catch ( InterruptedException x ) { }  
        print("leaving action()");  
    }  

    public void print(String msg) {  
        threadPrint("objID=" + objID + " - " + msg);  
    }  

    public static void threadPrint(String msg) {  
        String threadName = Thread.currentThread().getName();  
        System.out.println(threadName + ": " + msg);  
    }  

    public static void main(String[] args) {  
        final Deadlock obj1 = new Deadlock("obj1");  
        final Deadlock obj2 = new Deadlock("obj2");  

        Runnable runA = new Runnable() {  
                public void run() {  
                    obj1.checkOther(obj2);  
                }  
            };  

        Thread threadA = new Thread(runA, "threadA");  
        threadA.start();  

        try { Thread.sleep(200); }   
        catch ( InterruptedException x ) { }  

        Runnable runB = new Runnable() {  
                public void run() {  
                    obj2.checkOther(obj1);  
                }  
            };  

        Thread threadB = new Thread(runB, "threadB");  
        threadB.start();  

        try { Thread.sleep(5000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("finished sleeping");  

        threadPrint("about to interrupt() threadA");  
        threadA.interrupt();  

        try { Thread.sleep(1000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("about to interrupt() threadB");  
        threadB.interrupt();  

        try { Thread.sleep(1000); }   
        catch ( InterruptedException x ) { }  

        threadPrint("did that break the deadlock?");  
    }  
} 

運行結果如下:

http://wiki.jikexueyuan.com/project/java-concurrency/images/deadlock.jpg" alt="" />

從結果中可以看出,在執(zhí)行到 other.action() 時,由于兩個線程都在試圖獲取對方的鎖,但對方都沒有釋放自己的鎖,因而便產生了死鎖,在主線程中試圖中斷兩個線程,但都無果。

大部分代碼并不容易產生死鎖,死鎖可能在代碼中隱藏相當長的時間,等待不常見的條件地發(fā)生,但即使是很小的概率,一旦發(fā)生,便可能造成毀滅性的破壞。避免死鎖是一件困難的事,遵循以下原則有助于規(guī)避死鎖:

  • 只在必要的最短時間內持有鎖,考慮使用同步語句塊代替整個同步方法;
  • 盡量編寫不在同一時刻需要持有多個鎖的代碼,如果不可避免,則確保線程持有第二個鎖的時間盡量短暫;
  • 創(chuàng)建和使用一個大鎖來代替若干小鎖,并把這個鎖用于互斥,而不是用作單個對象的對象級別鎖。