惰性初始化造成的死鎖問題

下面我們來看看下面這段代碼,我們看看它會打印什麼東西出來。

package test85;
/**
 * 
 * @author 王新
 *這個程序會發生死鎖。。。。。。。。。。
 *詳細見 java解惑 85題
 */
public class Lazy {
    private static boolean initialized = false;
    static {
        Thread t = new Thread(new Runnable() {
                public void run() {
                    initialized = true;
                }
            });
        t.start();
        try{
            t.join();
        }catch (InterruptedException e){
            throw new AssertionError(e);
        }
    }
 
    public static void main(String[] args){
        System.out.println(initialized);
    }
}

我們看上面的代碼開始定義一個靜態的boolean initialized 其初始值爲false。然後再定義一個線程,在他的run方法中我們將 initialized  設置爲true,主線程啓動了後臺線程,然後有調用join()方法等待後臺線程結束,當後臺線程執行完後我們可以知道 initialized 被置爲true,然後我們在主方法中輸出 initialized 的值。但是我們的程序不會輸出任何的東西他應該是被掛起了。。。。

在解決這個問題我們需要知道一個初始化的細節。當一個線程訪問一個類的某個成員的時候,它會去檢查這個類是否已經被初始化。在一般情況下,我們有四種情況發生。

  • 這個類尚未被初始化。
  • 這個類正在被當前線程初始化:這是對初始化的遞歸請求。
  • 這個類正在被其他線程而不是當前線程初始化。
  • 這個類已經被初始化。
當主線程調用Lazy.main方法時,它會檢查Lazy類是否已經被初始化。此時它並沒有被初始化(情況1),所以主線程會記錄下當前正在進行初始化,並開始對這個類進行初始化。按照我們前面的分析,主線程會將initialized的值設爲false,創建並啓動一個後臺線程,該線程的run方法會將initialized設爲true,然後主線程會等待後臺線程執行完畢。此時,有趣的事情開始了。
那個後臺線程調用了它的run方法。在該線程將Lazy.initialized設爲true之前,它也會去檢查Lazy類是否已經被初始化。這個時候,這個類正在被另外一個線程進行初始化(情況3)。在這種情況下,當前線程,也就是那個後臺線程,會等待Class對象直到初始化完成。遺憾的是,那個正在進行初始化工作的線程,也就是主線程,正在等待着後臺線程運行結束。因爲這2個線程現在正相互等待着,該程序就死鎖了(deadlock)。這就是所有的一切,真是遺憾。有2種方法可以訂正這個程序。到目前爲止,最好的方法就是不要在類進行初始化的時候啓動任何後臺線程:有些時候,2個線程並不比1個線程好。

總之,在類的初始化期間等待某個後臺線程很可能會造成死鎖。要讓類初始化的動作序列儘可能地簡單。類的自動初始化被公認爲是語言設計上的難題,Java的設計者們在這個方面做得很不錯。如果你寫了一些複雜的類初始化代碼,很多種情況下,你這是在搬起石頭砸自己的腳。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章