關於java中死鎖的總結

關於死鎖,估計不少程序員都碰到過,而且有時候這種狀況出現以後的問題也不是很是好排查,下面整理的就是本身對死鎖的認識,以及經過一個簡單的例子來來接死鎖的發生,本身是作python開發的,可是對於死鎖的理解一直是一種模糊的概念,也是想過此次的整理更加清晰的認識這個概念。java


用來理解的例子是一個簡單的生產者和消費者模型,這裏是有一個生產者,有兩個消費者,而且注意代碼中使用notify方法的代碼行python

package study_java.ex11;

import java.util.LinkedList;
import java.util.List;

public class PCDemo1 {
    public static void main(String[] args){
        Pool pool = new Pool();
        Producter p1 = new Producter(pool);
        p1.setName("p1");
        Consumer c1 = new Consumer(pool);
        Consumer c2 = new Consumer(pool);
        c1.setName("c1");
        c2.setName("c2");
        p1.start();
        c1.start();
        c2.start();
    }
}

class Pool{
    private List<Integer> list = new LinkedList<Integer>();
    private int Max = 1;
    public void addLast(int n){
        String name = Thread.currentThread().getName();
        synchronized (this){
            while (list.size() >= Max){
                try{
                    System.out.println(name+".wait()");
                    this.wait();
                }
                catch (Exception e){
                    e.printStackTrace();
                }
            }
            System.out.println(name + "+" + n);
            list.add(new Integer(n));
            System.out.println(name + ".notify()");
            this.notify();   // 注意這裏是調用的是notify方法
        }
    }
    public int remove(){
        String name = Thread.currentThread().getName();
        synchronized (this){
            while (list.size() == 0){
                try{
                    System.out.println(name + ".wait()");
                    this.wait();
                }
                catch (Exception e){
                    e.printStackTrace();
                }

            }
            System.out.println(name + "-" + 0);
            int no = list.remove(0);
            System.out.println(name + ".notify()");
            this.notify();  // 注意這裏是調用的是notify方法
            return no;
        }
    }

}

// 生產者
class Producter extends Thread{
    private Pool pool;
    static int i = 1;
    public Producter(Pool pool){
        this.pool = pool;
    }
    public void run(){
        while (true){
            pool.addLast(i++);
            System.out.println("生產者生產了"+i+"號");
        }
    }

}


// 消費者
class Consumer extends Thread{
    private Pool pool;
    public Consumer(Pool pool){
        this.pool = pool;
    }
    public void run(){
        while (true){
            int no = pool.remove();
            System.out.println("消費者消費了"+no+"號");
        }
    }

}

這段代碼的運行效果是日誌,在最後程序卡主不動了:程序員

c1.wait()
p1+1
p1.notify()
c1-0
c1.notify()
消費者消費了1號
c1.wait()
生產者生產了2號
p1+2
p1.notify()
c1-0
c1.notify()
消費者消費了2號
c1.wait()
生產者生產了3號
p1+3
p1.notify()
c1-0
c1.notify()
消費者消費了3號
c1.wait()
生產者生產了4號
p1+4
p1.notify()
c1-0
c1.notify()
消費者消費了4號
c1.wait()
生產者生產了5號
p1+5
p1.notify()
c1-0
c1.notify()
消費者消費了5號
c1.wait()
生產者生產了6號
p1+6
p1.notify()
生產者生產了7號
c1-0
c1.notify()
消費者消費了6號
c1.wait()
p1+7
p1.notify()
生產者生產了8號
p1.wait()
c2-0
c2.notify()
消費者消費了7號
c2.wait()
c1.wait()
p1+8
p1.notify()
生產者生產了9號
p1.wait()
c2-0
c2.notify()
消費者消費了8號
c2.wait()
c1.wait()

對上面的出現卡主的狀況進行分析,理解爲啥會卡主:this

從此次的執行效果能夠看出第一次是c1搶到了執行權,可是這個時候pool是空
因此c1沒有能夠消費的對象,被放入到了等待隊列spa

接着p1搶到了執行權,生產了1個,而後p1.notify(),這個時候等待隊列裏只有c1,因此c1被喚醒,c1消費了1個,而後c1.notify(), 這個時候等待隊列也沒有等待的,這個時候有被c1搶到了執行權,可是pool裏沒有能夠消費的內容,因此c1.wait() 進入到等待隊列線程

這個時候p1搶到執行權,生產了1個,而後p1.notify(),這個時候等待隊列裏只有c1,因此c1被喚醒,c1也搶到了執行權,消費了1個,而後c1.notify()
一樣這個時候等待隊列裏沒有等待的,c1此次又搶到了執行權,但pool裏沒有能夠消費的內容,因此c1.wait(),進入到等待隊列日誌

p1 又搶到了執行權,生產1個,而後p1.notify(),這個時候等待隊列裏只有c1,因此c1被喚醒,c1也搶到了執行權,消費了1個,而後c1.notify()
一樣這個時候等待隊列裏沒有等待的,c1此次又搶到了執行權,但pool裏沒有能夠消費的內容,因此c1.wait(),進入到等待隊列code

.......這種狀況重複了幾回對象

可是運行到下面這段的時候問題出現了:blog

p1+7
p1.notify()
生產者生產了8號
p1.wait()
c2-0
c2.notify()
消費者消費了7號
c2.wait()
c1.wait()
p1+8
p1.notify()
生產者生產了9號
p1.wait()
c2-0
c2.notify()
消費者消費了8號
c2.wait()
c1.wait()

繼續進行分析,中間重複的部分不作分析了,和前面的過程是同樣的

這個時候等待隊裏裏依然是c1 這個時候p1搶到了執行權,生產了1個,p1.notify() 這個時候等待隊列裏只有c1,因此c1被喚醒,可是c1沒有搶過p1,p1本身又搶到了執行權,可是這個時候pool裏面已經有內容,因此p1沒有生產,p1.wait(),p1進入等待隊列

這個時候c2搶到了執行權,c2消費1個,c2.notify() 這個時候等待隊裏是p1,p1被喚醒,可是這個時候c2搶到了執行權,可是pool沒有內容能夠消費因此c2.wait() 進入等待隊列

接着c1搶到了執行權,一樣pool沒有能夠消費的內容,c1.wait() 進入到等待隊列

p1這個時候搶到了執行權,p1生產了1個,接着p1.notify() 這個時候等待隊列裏有c1和c2,可是隻有一個會被喚醒,無論是哪一個,結果沒搶過p1,p1再次拿到執行權,可是這個時候pool已經有內容,因此p1.wait() p1進入等待隊列

從下面是c2執行,能夠看出剛纔是c2被喚醒了,這個時候c2也拿到了執行權消費了1個。c2.notify() 等待隊列裏這個時候有c1 和p1 可是這個時候c2 本身搶到了執行權,可是沒有能夠消費的,c2.wait() c2 進入等待隊列
不巧的是剛纔搶到執行權的正好是c1,因此c1繼續wait,再次進入等待隊列

到這個時候p1 c1 c2 都進入等待隊列裏,都在等待喚醒,也就出現了程勳最後卡住不動的狀況

 

解決的方法有兩種:

第一種:
其實解決上面的方法也比較簡單,就是把調用notify的地方所有換成notifyAll方法

notify和notifyAll的區別是,當執行notifyAll的時候會喚醒全部等待的線程,從而避免以前的都在等待隊列等待的問題

第二種:就是wait()的時候加上超時參數,不是像以前一直傻等,而是在超過既定的時間以後本身喚醒

相關文章
相關標籤/搜索