在實作多執行緒控管的程式時,最擔心的就是出現Dead Lock了。以下就特別示範了可能會發生DeadLock的程式:

上述的MyThread類別繼承了Thread類別,並在run方法中實作了執行緒要運作的程式。在這段run方法中間接的出現了巢狀的synhronized區塊。首先run方法呼叫doSomething1()時進入第一個synchronized區塊並在此鎖定myLock1物件,再輸出一行字串後,doSomething1()又呼叫了doSomething2()。此時進入第二個synchronized區塊並在此鎖定了myLock2物件,接著又輸出一行字串。然後doSomething2()方法結束,doSomething1()方法結束,run()方法也結束。執行緒就結束了。
看到這裡可能不覺得這段程式會發生DeadLock,其實造成Deadlock還要透過main方法助我們一臂之力!
在Main類別提供的main方法就寫了一段極可能發生DeadLock的程式。在這裡建立了兩個Object類別的物件:obj1與obj2,又建立了MyThread類別的物件t1,並且將obj1, obj2傳入t1建構子中;接著建立了MyThread類別的物件t2,並且反過來將obj2, obj1傳入t2建構子中。
假設程式執行這個main方法,在啟動了t1, t2執行緒後,若排程的執行順序如下就會發生Deadlock:
排程系統 |
執行緒t1 |
執行緒t2 |
選擇執行t1 |
t1.run呼叫
26行的t1.doSomething1,
執行27行 鎖定obj1給t1專用,
執行28行輸出訊息,
執行29行呼叫t1.doSomething2,
執行第34行前排程決定執行其他執行緒; |
|
將t1推回待執行區,改選t2 |
|
t1.run呼叫
26行的t1.doSomething1,
執行27行 鎖定obj2給t2專用,
執行28行輸出訊息,
執行29行呼叫t2.doSomething2,
執行第34行時想鎖定obj1給t2用,
但obj1已屬於執行緒t1,
排程決定將t2從執行區移到obj1物件等候區; |
排程只能挑選t2 |
從34行繼續執行:
34行想鎖定obj2給t1用
但obj2已屬於執行緒t2,
排程決定將t1從執行區移到obj2物件等候區;
所以t2在等t1鎖定的obj1,t1在等t2鎖定的obj2,發生Deadlock |
|
但執行時,排程系統不一定照我們所想的執行,所以DeadLock不容易被發現!這時透過IDE的Debug功能可以讓這個DeadLock狀況真的被看到!
首先,在程式的第27行、第34行設定中斷點,接著從選單的[Debug]功能選擇[Debug Project]用Debug模式來執行這個程式!

如此這個程式就會開始在Debug模式中執行,兩個執行緒執行到第27行時也會因為中斷點而暫停(可從 [Debugging]視窗看到,如下圖):

接下來,可以用Debug的[Step Over]先從t1執行緒逐步執行第27、28、29,到34行前時,改去執行t2執行緒的第27、28、29行,執行了34行後就會看到Debug工具顯示的提示畫面,表示t2因為所需的物件obj1被t1鎖定而被阻擋:

所以我們只好去執行t1停在34行的程式,在執行t1的34行時,就會看到t1因為所需的物件obj2被t2鎖定而被阻擋。這兩個執行緒發生DeadLock!當Java應用程式發生DeadLock,只能強迫結束而沒有其他更好的作法!所以開發多執行緒程式時要小心避免這樣的情況!
|