Java 惰性初始化
publicclass Lazy {
privatestaticbooleaninitial = false;
static {
Thread t = new Thread(new Runnable() {
publicvoid run() {
System.out.println("befor...");//此句會輸出
/*
* 由于使用Lazy.initial靜態成員,又因為Lazy還未初
* 始化完成,所以該線程會在這里等待主線程初始化完成
*/
initial = true;
System.out.println("after...");//此句不會輸出
}
});
t.start();
try {
t.join();// 主線程等待t線程結束
} catch (InterruptedException e) {
e.printStackTrace();
}
}
publicstaticvoid main(String[] args) {
System.out.println(initial);
}
}
看看上面變態的程序,一個靜態變量的初始化由靜態塊里的線程來初始化,最后的結果怎樣?
當一個線程訪問一個類的某個成員的時候,它會去檢查這個類是否已經被初始化,在這一過程中會有以下四種情況:
1、 這個類尚未被初始化
2、 這個類正在被當前線程初始化:這是對初始化的遞歸請求,會直接忽略掉(另,請參考《構造器中靜態常量的引用問題》一節)
3、 這個類正在被其他線程而不是當前線程初始化:需等待其他線程初始化完成再使用類的Class對象,而不會兩個線程都會去初始化一遍(如果這樣,那不類會初始化兩遍,這顯示不合理)
4、 這個類已經被初始化
當主線程調用Lazy.main,它會檢查Lazy類是否已經被初始化。此時它并沒有被初始化(情況1),所以主線程會記錄下當前正在進行的初始化,并開始對這個類進行初始化。這個過程是:主線程會將initial的值設為false,然后在靜態塊中創建并啟動一個初始化initial的線程t,該線程的run方法會將initial設為true,然后主線程會等待t線程執行完畢,此時,問題就來了。
由于t線程將Lazy.initial設為true之前,它也會去檢查Lazy類是否已經被初始化。這時,這個類正在被另外一個線程(mian線程)進行初始化(情況3)。在這種情況下,當前線程,也就是t線程,會等待Class對象直到初始化完成,可惜的是,那個正在進行初始化工作的main線程,也正在等待t線程的運行結束。因為這兩個線程現在正相互等待,形成了死鎖。
修正這個程序的方法就是讓主線程在等待線程前就完成初始化操作:
publicclass Lazy {
privatestaticbooleaninitial = false;
static Thread t = new Thread(new Runnable() {
publicvoid run() {
initial = true;
}
});
static {
t.start();
}
publicstaticvoid main(String[] args) {
// 讓Lazy類初始化完成后再調用join方法
try {
t.join();// 主線程等待t線程結束
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(initial);
}
}
雖然修正了該程序掛起問題,但如果還有另一線程要訪問Lazy的initial時,則還是很有可能不等initial最后賦值就被使用了。
總之,在類的初始化期間等待某個線程很可能會造成死鎖,要讓類初始化的動作序列盡可能地簡單。
原文鏈接:http://www.daimami.com/java-other/117673.htm