演習 1.4: スレッド・ボトルネックの解決
この演習を開始する前に、『演習 1.3: スレッド・ボトルネックの特定』を
完了する必要があります。
コード内のデッドロックの場所を特定するには、スレッド・ビューに加えて、UML2 シーケンス図 (「オブジェ
クト相互作用」ビューと「スレッド相互作用」ビュー) および呼び出しスタック・ビューを使用できます。
このデッドロックを解決するには、まずどのメソッド呼び出しとどのオブジェクトがこの問題に関連してい
るのかを以下のようにして調べます。
- スレッド・ビューで、最初に「Deadlock」状態になった philo* スレッドを探す。
カーソルを「Deadlock」セグメント上で停止します。
ツールチップが表示されて、ロック (Fork.<id number>) とロック元スレッド (Locking
Thread.<name>) が特定されます。次に例を示します。
Lock: Fork 10038
Locking Thread: philo#1
- 実行用のプロファイル・リソースを右クリックして、「アプリケーションから開く」>「
UML2 オブジェクト相互作用」を選択する。「UML2 シーケンス図」ビューが開いて、「オブジェクト相互作用」が表示
されます。
- シーケンス図で、ビューを横方向にスクロールして Fork.<id number> を探して、クリッ
クして選択する。

- スクロールダウンして、いずれかの philo* スレッドから Fork.<id number>
に伸びている水平方向の矢印を探す。これらの矢印ではオブジェクト間の相互作用が示され、これら 2 つのオブジェクト間
の最初の相互作用では、その philo* スレッドが Fork.<id number> を獲得したことが
示されます。
このような矢印には、getName というラベルが付いています。

- getName をクリックする。スレッド・ビューで、垂直方向の「現在時刻」インディケーターが移動し
て、getName が呼び出されたときにプログラム全体で起こっていた状況が示されます。
要求元の philo* スレッドが「Waiting for Lock」状態にも
「Deadlock」状態にもまだなっていないため、要求が成功したことがわかります。

- シーケンス図で、Fork.<id number> をもう一度クリックし、スクロールダウンして、別
の philo* スレッドで発行された getName 要求を探す。
- この getName のインスタンスをクリックする。「現在時刻」インディケーターでは、要求元の
philo* スレッドが Fork.<id number> を獲得しておらず、「Waiting
for Lock」状態になってから「Deadlock」状態に移行したことが示されます。
このインスタンスでは、別のスレッドによって保持されている Fork への要求が問題となっています。
デッドロック状態になっている他のスレッドをチェックして、このことが他のインスタンスでも当てはまる
ことを確認してください。
次に、問題の原因となっているメソッドを探しましょう。
- 実行用のプロファイル・リソースを右クリックして、「アプリケーションから開く」>「
UML2 スレッド相互作用」を選択する。「UML2 シーケンス図」ビューが開いて、「スレッド相互作用」が表示
されます。
- スレッド・ビューで、メニューのドロップダウン・ボタンをクリックして、「呼び出しスタック・
ビューを開く (Open Call Stack View)」をクリックする。
- スレッド・ビューで、スレッドの名前が表示されている場所で、デッドロック状態になっている最初の
philo* スレッドをダブルクリックする。
「スレッド相互作用」ビューが変更され、そのスレッドの情報のみが表示されます。
- そのスレッドの情報の最後までスクロールダウンして、そのスレッドで実行された最後のメソッドである
run メソッドをダブルクリックする。呼び出しスタック・ビューでは、その時点のスタック
上のすべての呼び出しが表示されます。
- 呼び出しスタックで、ロックを保持しているスレッドが Philosopher.java 内の
Sleep メソッドを呼び出していることに注目する。つまり、このスレッドもデッドロック
状態になっており、その結果として動作を停止しています。
- デッドロック状態になって実行を終了している他のスレッドをチェックする。
Philosopher.java 内の Sleep メソッドは、多くの場合呼び出しスタック内にあり、問題を引
き起こしている可能性があります。
次に、Sleep メソッドが問題の原因であると想定します。
コードを見てみましょう。
- 呼び出しスタックで、Sleep(int) void [Philosopher.java] のインスタンスを右クリック
して、「ソースを開く」を選択する。Sleep クラスのロケーションに、ソースがエディターで開かれます。
- コードを調べる。
Sleep メソッドは run メソッドによって呼び出されることに注目してください。
最初に「got left...」というメッセージを出力する trace が呼び出され、次に Sleep が呼び出されます。Sleep への呼び出しをコメ
ント化することにより、デッドロックを防止できる可能性があります。
- Sleep をコメント化する。
- 「ファイル」>「保管」を選択する。
もう一度プログラムのプロファイルを作成します。
今度は、デッドロックが発生することなくプログラムが実行されて、コンソールに次のメッセージが
出力されます。
HeadWaiter reports all philosophers have finished dining normally
これまでの説明からわかるように、スレッド・ビューとその他のビューでは、プログラムの実行時に
各スレッドで起こっている状況が表示されます。
プログラムの知識に基づいて、分析を実行してデッドロックを解決することはプログラマーの責任です。
チュートリアルの締めくくりとして、『要約』で学習内容を再確認
してください。