練習 1.4:解決執行緒瓶頸

開始之前,您必須先完成練習 1.3:識別執行緒瓶頸

若要尋找程式碼中的死結,除了「執行緒視圖」, 您還可以使用「UML2 序列圖」(「物件互動」及「執行緒互動」視圖)以及「呼叫堆疊視圖」。

為了解決這個死結,讓我們先查出問題中所涉及的方法呼叫和物件:

  1. 在「執行緒視圖」中, 尋找第一個進入「死結」狀態的 philo* 執行緒。將游標暫停在「死結」區段上。工具要訣會識別鎖定(分出.<ID 號碼>)及鎖定的執行緒(鎖定的執行緒.<名稱>),例如:

鎖定:分出 10038
鎖定的執行緒:philo#1

  1. 用滑鼠右鍵按一下此程式執行的側寫資源, 然後選取開啟工具 > UML2 物件互動。這時會開啟「UML2 序列圖」視圖,顯示物件互動。
  2. 在序列圖中,水平捲動視圖,以尋找分出.<ID 號碼>,再按一下將它選取。

序列圖顯示物件互動

  1. 向下捲動,在其中一個 philo* 執行緒至分出.<ID 號碼> 之間尋找水平箭頭。這些箭頭會顯示物件之間的互動, 並且兩個物件之間的第一個互動會指出 philo* 執行緒已獲得分出.<ID 號碼>。您將會發現含 getName 標籤的箭頭。

序列圖顯示 getName

  1. 按一下 getName。在「執行緒視圖」中, 垂直的「現行時間」指標會移動, 以顯示 getName 被呼叫時整個程式中的發生情況。您會看到要求已順利完成, 因為提出要求的 philo* 執行緒尚未處於「等待鎖定」或「死結」狀態。

「執行緒」視圖顯示 philo* 執行緒為執行中狀態

  1. 在序列圖中,再次按一下分出.<ID 號碼>,然後向下捲動, 以尋找源自不同 philo* 執行緒的 getName 要求。
  2. 按一下這個 getName 實例。「現行時間」指標會顯示, 提出要求的 philo* 執行緒未取得分出.<ID 號碼>, 而是進入「等待鎖定」,接著進入「死結」狀態。

在這個實例中,對另一個執行緒所持有之分出的要求是問題所在。 檢查其他處於死結狀態的執行緒,以驗證其他實例中是否也是這個狀況。

現在,讓我們尋找造成問題的方法:

  1. 用滑鼠右鍵按一下此程式執行的側寫資源, 然後選取開啟工具 > UML2 執行緒互動。這時會開啟「UML2 序列圖」視圖,顯示執行緒互動。
  2. 在「執行緒視圖」中,按一下功能表下拉按鈕, 再按一下開啟呼叫堆疊視圖
  3. 在顯示執行緒名稱的「執行緒視圖」中, 按兩下第一個在死結狀態的 philo* 執行緒。請注意,「執行緒互動」視圖會變更成只顯示該執行緒的資訊。
  4. 向下捲動至執行緒資訊結尾, 然後按兩下執行緒執行的最後一個方法:run 方法。「呼叫堆疊視圖」會顯示當時在堆疊上的所有呼叫。
  5. 在「呼叫堆疊」中, 請注意,持有鎖定的執行緒已呼叫 Philosopher.java 中的 Sleep 方法; 或者它也是處於死結狀態,因此不會執行任何動作。
  6. 檢查在程式執行結束之後處於死結狀態的其他執行緒; Philosopher.java 中的 Sleep 方法常出現在「呼叫堆疊」中,可能是問題所在。

我們現在懷疑 Sleep 方法是問題所在。讓我們查看程式碼:

  1. 在「呼叫堆疊」中,用滑鼠右鍵按一下 Sleep(int) void [Philosopher.java] 實例, 然後選取開啟程式碼。在編輯器中會開啟程式碼,並移到 Sleep 類別的位置。
  2. 查驗程式碼。請注意,Sleep 方法是由 run 方法所呼叫的。首先會呼叫 trace, 它會印出訊息 "got left...",然後再呼叫 Sleep。透過註銷對 Sleep 的呼叫,我們可能可以防止死結。
  3. 註銷 Sleep
  4. 選取檔案 > 儲存
現在,再次側寫程式。

這次,程式執行就不會發生死結,並且會寫入主控台:

HeadWaiter reports all philosophers have finished dining normally

您可以清楚看到,「執行緒視圖」和其他視圖會顯示程式執行時執行緒的發生情況。您可以根據對程式的知識, 自行決定是否要執行分析並解決死結。

請檢視摘要中的資料來完成您的指導教學。

讀者意見
(C) Copyright IBM Corporation 2000, 2005. All Rights Reserved.