靜態初始化塊裡啟動新執行緒的陷阱

2021-08-10 12:39:04 字數 2398 閱讀 5149

靜態初始化塊裡啟動新執行緒的陷阱

一:問題的提出

我們知道靜態塊幫助我們完成一些類的初始化的工作,那麼在靜態塊裡面啟動乙個執行緒,讓它來幫助我們來完成初始化的工作會發生什麼哪?死鎖還是輸出了讓我們感覺不是我們想要的結果吶?,不妨試試看,結果是什麼吧.

二:具備的知識

多執行緒的基礎知識, 多執行緒的建立,啟動,join()方法和匿名內部類的使用等.例子比較簡單的.   

三:寫**前的分析

我們知道,main執行緒開始執行對類的初始化的時候,初始化的主要的步驟就是兩個

(1):為這個類的所有的靜態的變數分配記憶體空間.

(2):呼叫靜態初始化塊**執行初始化.

注意這兩者之間的順序喲.

當某乙個執行緒訪問乙個靜態變數的時候,這個類的狀態會分大概四種情況的:

(1):該類尚未被初始化,當前執行緒開始對其進行初始化.

(2):該類正在執行初始化,當前執行緒就會 遞迴的執行初始化.

(3):這個類正在被其他的執行緒執行初始化,當前的執行緒就會暫停,等待其他的執行緒初始化完成.

(4):這個類已經初始化完成啦,就會直接得到這個靜態變數的值 

四:案例**

public class staticthread

};thread.start();

//讓當前的main執行緒等待新執行緒執行完畢

try catch (interruptedexception e)

}//定義乙個靜態的變數,設定初始值

}  執行結果:

看看結果發生了死鎖.(兩個執行緒互相等待對方執行,因此都不會像下執行).

分析死鎖發生的原因:

首先是mian執行緒開始對這個類進行初始化,對靜態的變數分配記憶體空間,這個時候的值為null,然後就會執行那個靜態初始化塊.在靜態初始化塊裡面建立乙個執行緒,並且啟動了這個新執行緒,並且呼叫了join的方法.這就意味著mian執行緒必須等待新執行緒執行完畢後才可以向下執行的.(靜態變數屬於類),但是這個類正在由main執行緒初始化,因此新的執行緒就會暫停等待main執行緒對這個類執行初始化結束的.不出意外的出現了死鎖.

新的執行緒開始執行之後,首先會按照執行緒體裡面的**順序執行.看到輸出的那句話後,接下來就是開始訪問那個靜態變數。

接下來我們去掉那個join()方法的呼叫,不讓mian執行緒等待新執行緒嘛.

public class staticthread

};thread.start();

}//定義乙個靜態的變數,設定初始值

執行結果:

發現那個靜態塊裡面的修改絲毫沒有起作用

很顯然,那個新執行緒是在類初始化後完成的.呼叫了start()只是進入了就緒狀態,雖然執行緒修改了那個靜態變數的值,但是陳序不在訪問它了.

可以在新增乙個thread.sleep(10)給新執行緒乙個表現自己的機會嘛,畢竟主線程執行類的初始化也挺累的.但是就算給新執行緒機會了,輸出的結果還是一樣的,沒有變化,只因為主線程對靜態變數的初始化還沒有完成的.而新執行緒又要訪問那個變數的.這個在前面分析過了,當前的新執行緒要暫停等待main執行緒來完成剩下的初始化工作的.直到完成為止.main執行緒對那個類的初始化工作完成後,那個靜態變數的值才是那個值,執行main()方法的第一行**就輸出了那個值.這是才切換到了新的執行緒繼續執行執行緒體裡面的**.

發現一點:靜態初始化塊裡面啟動多執行緒對靜態變數所賦的值不是初始化的,而是一次普通的賦值操作.

測試**如下:

public class staticthread

};thread.start();

}//定義乙個靜態的變數,設定初始值

static string website;

public static void main(string args)  }

執行結果:

為了更加清楚的說明可以加乙個final修飾符,發現編譯不通過.無法為最終的變數賦值。

五:簡單總結

靜態塊裡面的**不一定都是類的初始化的,靜態塊的啟動新的執行緒的就是新的執行緒方法執行體的,並不是類的初始化!相同的道理:放在非靜態塊裡面的新執行緒的run()方法也就是執行緒的執行體而已,不參與物件的初始化工作的.  算是多執行緒結合靜態塊的使用的一點陷阱吧,注意一點就避免乙個bug吧. 

}

初始化塊和靜態初始化塊

初始化塊的使用 變數,靜態變數,方法,靜態方法 由圖可知變數沒問題 上面的方法不可行能否初始化靜態方法 嘗試使用初始化塊初始化構造方法 最後比較一下構造方法初始化,初始化塊初始化,靜態初始化塊初始化當中誰最先被初始化 package staticinitializelump public class...

靜態初始化塊 初始化塊 構造方法

1.所有的靜態初始化塊都優先執行,其次才是非靜態的初始化塊和建構函式,它們的執行順序是 1 父類的靜態初始化塊 2 子類的靜態初始化塊 3 父類的初始化塊 4 父類的建構函式 5 子類的初始化塊 6 子類的建構函式 注意 1 此處的構造方法需要與自己的類名相同,2 靜態 初始化塊需要用 2.構造方法...

static靜態初始化塊

j a 中可以通過初始化塊進行資料賦值。如 在類的宣告中,可以包含多個初始化塊,當建立類的例項時,就會依次執行這些 塊。如果使用 static 修飾初始化塊,就稱為靜態初始化塊。需要特別注意 靜態初始化塊只在類載入時執行,且只會執行一次,同時靜態初始化塊只能給靜態變數賦值,不能初始化普通的成員變數。...