Java 惰性初始化

xiangkun 11年前發布 | 2K 次閱讀 云計算

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);

      }

}

雖然修正了該程序掛起問題,但如果還有另一線程要訪問Lazyinitial時,則還是很有可能不等initial最后賦值就被使用了。

總之,在類的初始化期間等待某個線程很可能會造成死鎖,要讓類初始化的動作序列盡可能地簡單。

原文鏈接:http://www.daimami.com/java-other/117673.htm

 本文由用戶 xiangkun 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!