デッドロックについて

デッドロックは、プログラム内の1つ以上のスレッドがリソースにアクセスできなくなるか、待機状態のままいつまでも処理できない場合に発生します。Javaでの一般的なデッドロックは、モニター・ブロック・サイクル・デッドロックです。

モニター・ブロック・サイクル・デッドロックは、複数のスレッドが、他のスレッドがすでに実行中の同期コードを実行するために待機し、処理ができなくなる場合に発生します。

次に典型的なJavaの同期デッドロックの例を示します。

スレッド1は次のコードを実行しています。


synchronized (a) { ... synchronized (b) { ... } ... }

同時に、スレッド2は次のコードを実行しています。


synchronized (b) { ... synchronized (a) { ... } ... }

スレッド2がsynchronized (b)を実行中、スレッド1がsynchronized (a)に入るとデッドロックが発生します。スレッド1は、スレッド2がsynchronized (b)を終了するまでsynchronized (b)に入ることができず、スレッド2は、スレッド1がsynchronized (a)を終了するまでsynchronized (a)に入ることができません。デッドロックは「デッドリ・エンブラス」とも呼ばれます。この例ではスレッドは2つですが、スレッドの数が3つ以上ある場合にも同じような状況が発生します。デッドロック・ブレークポイントでは、このようなタイプのデッドロックを検出できます。

1つのスレッドが特定のオブジェクトでwaitメソッドをコールし、他のスレッドがそのオブジェクトでnotifyメソッドをコールしない場合にもデッドロックが発生します。このタイプのデッドロックの最も一般的な原因はタイミングです。待機側のスレッドがwaitをコールする前に、通知側のスレッドがnotifyをコールした可能性があります。waitをコールする場合に注意が必要な点は、notifyが前に何度もコールされていたとしても、waitメソッドはnotifyがもう一度コールされるまで待機するという点です。また、待機しているスレッドがない場合、notifyはエラーを返しません。デッドロック・ブレークポイントでは、このタイプのデッドロックは検出できません。

プログラムがハングしている可能性がある場合は、デバッガで「プログラムの停止」(pause_button)を使用してプログラムを一時停止し、モニター・ウィンドウを開きます。コードを調べて、待機しているスレッドを見つけます。最初のスレッドがwaitをコールする前に別のスレッドがnotifyをコールしている場合はデッドロックが発生しています。このタイプのデッドロックの検出は非常に困難です。他のどのスレッドがnotifyをコールしたかを検出するには、作成したコードをよく理解しておく必要があります。

デッドロック・ブレークポイントについて

JDeveloperのデバッガでは、実行を開始すると永続デッドロック・ブレークポイントが設定されます。デッドロック・ブレークポイントは、デッドロックのソースの場所を特定するのが困難な場合に便利です。デバッガは、デッドロック・ブレークポイントに達すると停止します。デバッガは、前述のモニター・ブロック・サイクル・デッドロックを検出できます。モニター・ウィンドウは、デッドロックを処理する場合に有益です。

デッドロック・ブレークポイントには、次の特徴があります。

JDeveloperのデバッガでは、永続デッドロック・ブレークポイントが自動的に作成されます。このブレークポイントは、モニター・ブロック・サイクルが検出されるたびに発生します。永続ブレークポイントは削除できません。新規デッドロック・ブレークポイントの作成はできませんが、既存の永続デッドロック・ブレークポイントの編集は可能です。

デッドロック検出がサポートされていないJava Virtual Machineもあります。たとえば、HotSpot VMでは、デッドロック検出はサポートされていません。


関連項目

モニター・ウィンドウについて
ブレークポイント・ウィンドウについて