Java 死鎖

我參與的幾個項目中,死鎖貌似未做太多考慮,可能項目對併發的安全和性能要求不高吧。但是在面試的時候,死鎖的問題依然常常被提起。這裏就簡單介紹下造成死鎖的原因及避免方法。

假如有2個線程,一個線程想先鎖對象1,再鎖對象2,恰好另外有一個線程先鎖對象2,再鎖對象1。
在這個過程中,當線程1把對象1鎖好以後,就想去鎖對象2,但是不巧,線程2已經把對象2鎖上了,也正在嘗試去鎖對象1。
什麼時候結束呢,只有線程1把2個對象都鎖上並把方法執行完,並且線程2把2個對象也都鎖上並且把方法執行完畢,那麼就結束了,但是,誰都不肯放掉已經鎖上的對象,所以就沒有結果,這種情況就叫做線程死鎖。下面有個實例程序模擬死鎖:

package org.thread;
public class Synchron {

    public void begin() {
        Thread t1 = new Thread(new Thread1());
        Thread t2 = new Thread(new Thread2());
        t1.start();
        t2.start();
    }

    public static void main(String[] args) {
        new Synchron().begin();
    }

    public synchronized void getI() {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException ex) {
            ex.printStackTrace();
        }
        getJ();
    }

    public synchronized void getJ() {
        getI();
    }

    class Thread1 implements Runnable {
        public void run() {
            getI();
        }
    }

    class Thread2 implements Runnable {
        public void run() {
            getJ();
        }
    }
}

那麼我們需要注意的是,線程死鎖的原因不是因爲相互調用,而是由線程對資源的佔用和等待導致的!

有一種避免死鎖的辦法是,儘可能鎖大的對象,即加大鎖的粒度;也應避免同時鎖多個對象。

再簡單說說wait和notify:

1).wait
Object類中的final方法,有InterruptedException。它的作用是導致當前的線程等待,直到其它線程調用此對象的notify方法或者notifyAll方法,wait還有一些重用方法,傳參數,比如說時間長度。
當前的線程必須擁有此對象監視器,然後該線程發佈對此監視器的所有權並且開始等待,直到其它線程通過調用notify方法或者notifyAll方法,通知在此對象的監視器上等待的線程醒來,然後該線程將等到重新獲得對監視器的所有權後才能開始執行。
說說wait和sleep的區別
首先sleep
sleep是Thread裏面的方法,在被執行的時候,鎖並不會被交出去,要直到sleep所在的方法全部被執行完畢以後才交出鎖。
wait是Object裏面的方法,在被執行的時候,鎖被解除,由其它線程去爭奪,直到有notify或者notifyAll方法喚醒它。

2).Notify
也是Object類中的方法,用於喚醒在此對象上等待着的某一個線程,如果有很多線程掛起的話,就隨機地決定哪一個。注意,是隨機的,這時可以用notifyAll來喚醒所有的。一定要注意這個問題,除非你明確地知道你在做什麼,否則最好就是用notifyAll。

注意事項:
wait()和notify()必須包括在synchronized代碼塊中,等待中的線程必須由notify()方法顯式地喚醒,否則它會永遠地等待下去。很多人初級接觸多線程時,會習慣把wait()和notify()放在run()方法裏,一定要謹記,這兩個方法屬於某個對象,應在對象所在的類方法中定義它,然後run中去調用它。

發佈了37 篇原創文章 · 獲贊 33 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章