演習 1.4: スレッドのボトルネックの解決
始める前に、『演習 1.3: スレッドのボトルネックの識別』を完了する必要があります。
コードのデッドロックを突き止めるには、スレッド・ビューに加えて、プロファイル・モニターの UML2 シーケンス・ダイアグラム (オブジェクト対話ビューおよびスレッド対話ビュー) およびコール・スタック・プロファイル・リソースが使用できます。
このデッドロックを解決するには、まず、呼び出しを行っているメソッドと、問題に関与するオブジェクトがどれかを検出します。
- スレッド・ビューで「ロック待ち」状態に入っている先頭の philo* スレッドを見つけます。カーソルを「ロック待ち」セグメント上に停止させます。スレッドが待っているロックが、ツールの説明によって識別されます (Fork.<id number>)。
- プロファイル・リソースを右マウス・ボタン・クリックし、「アプリケーションから開く」>「UML2 オブジェクト対話」を選択します。「UML2 シーケンス・ダイアグラム」ビューが開き、オブジェクト対話が表示されます。
- シーケンス・ダイアグラムでビューをスクロールして Fork.<id number> を見つけ、ダブルクリックして選択します。
- philo* スレッドの 1 つから Fork.<id number> への水平方向の矢印が見つかるまでスクロールダウンします。これらの矢印はオブジェクト間の相互作用を示し、これら 2 つのオブジェクト間での最初の相互作用は、philo* スレッドが Fork.<id number> を獲得したことを示します。getName というラベルの付いた矢印を見つけます。
- getName をダブルクリックします。スレッド・ビューで現在の時刻インディケーターを垂直方向に動かすと、getName が呼び出されたときに、プログラム全体で何が発生したかを参照できます。要求を行った philo* スレッドは「ロック待ち」状態に入っていないため、要求は成功したことが確認できます。
- シーケンス・ダイアグラムで Fork.<id number> を再度ダブルクリックし、別の philo* スレッドから発生した getName 要求が見つかるまでスクロールダウンします。
- getName のこのインスタンスをダブルクリックします。現在の時刻インディケーターには、要求を作成した philo* スレッドが Fork.<id number> を取得せず、「ロック待ち」状態に入ったことが示されます。
このインスタンスでは、別のスレッドによって保持されている Fork への要求が問題となっています。「ロック待ち」状態で終了した他のスレッドを確認して、このことが他のインスタンスにも該当するか検証します。
では、問題の原因となっているメソッドを見つけましょう。
- 実行するプロファイル・リソースを右マウス・ボタン・クリックし、「アプリケーションから開く」>「UML2 スレッド対話」を選択します。「UML2 シーケンス・ダイアグラム」ビューが開き、スレッド対話が表示されます。
- プロファイル・モニターでプロファイル・リソースを展開し、スレッド分析エントリーおよびコール・スタック・エントリーを展開します。
- スレッドの名前が表示されているスレッド・ビューで、「ロック待ち」状態に入っている先頭の philo* スレッドをダブルクリックします。「スレッド対話」ビューが、そのスレッドについてのみの情報を表示するように変更されます。
- スレッド情報の最後までスクロールダウンし、スレッドが実行した最後のメソッド (run メソッド) をダブルクリックします。プロファイル・モニターのコール・スタックに、そのときスタックにあるすべての呼び出しが表示されます。
- コール・スタックでは、ロックを保持しているスレッドは Philosopher.java の Sleep メソッドを呼び出したこと、またスレッドはロックを待機しており、結果的には何もしないことに注意します。
- ロックを待った状態で実行を終了した別のスレッドを確認します。Philosopher.java の Sleep メソッドは通常コール・スタック中にあり、これが問題となっています。
Sleep メソッドが疑わしいと思われます。ではコードを見てみましょう。
- コール・スタックで Sleep(int) void [Philosopher.java] のインスタンスを右マウス・ボタン・クリックし、「ソースを開く」を選択します。Sleep クラスの位置にあるソースがエディターで開きます。
- コードを調査してください。Sleep メソッドは run メソッド内部から呼び出されたことに注意します。まず trace への呼び出しが存在し、これが「got left...」というメッセージを出力し、その後 Sleep への呼び出しが存在します。Sleep への呼び出しをコメントにして取り除くことで、デッドロックが防止できる可能性があります。
- Sleep をコメントにして取り除きます。
- 「ファイル」>「保管」を選択します。
プログラムのプロファイルを再度作成します。
今回はデッドロックなしに実行され、コンソールに書き出します。
すべての philosopher が正常に実行を終了したことが HeadWaiter に報告されます。
お分かりの通り、スレッド・ビューと他のビューは、ご使用のプログラム実行時にスレッドに発生した内容を示します。次は、ご使用のプログラムに関する知識に基づいて分析を行い、デッドロックを解決します。
『要約』に記載されている資料を確認してチュートリアルを完了します。