複数のスレッドを実行するJavaアプリケーションをデバッグしています。ログをしばらく見てみると、それらのスレッドの1つがもう実行されていないようです。私の推測では、スレッドは決して解放されないロックを待機しています(最後の出力は同期メソッドを呼び出す前です)。
スレッドにタイムアウトを設定できますか?一種の「このロックを待ちますが、10秒後にロックが利用できなくなった場合は、もう待たないでください!」</ p>
複数のスレッドを実行するJavaアプリケーションをデバッグしています。ログをしばらく見てみると、それらのスレッドの1つがもう実行されていないようです。私の推測では、スレッドは決して解放されないロックを待機しています(最後の出力は同期メソッドを呼び出す前です)。
スレッドにタイムアウトを設定できますか?一種の「このロックを待ちますが、10秒後にロックが利用できなくなった場合は、もう待たないでください!」</ p>
固有のロックの代わりにjava.util.concurrent.Lockを使用できます。公正な順序付けのないRentrantLockは、組み込みロックと同じ基本的な動作とセマンティクスを持ちます。タイムアウト パラメータを取るメソッドがあります。Object
tryLock
Lock lock = ...;
if (lock.tryLock(10L, TimeUnit.SECONDS)) {
try {
// manipulate protected state
} finally {
lock.unlock();
}
} else {
// perform alternative actions
}
デバッグ用のコードを追加するのではなく、デバッグツールまたはプロファイラーを使用できます。
1つのオプションは、「デッドロックの検出」と呼ばれるボタンを含むJConsole(JDKに付属)のようなものを使用することです(少なくともJava 6では機能しますが、Java 5では機能しないと思います)。もう1つのオプションは、コンソールへのスレッドダンプを生成することです。Unixでは「kill-3」と入力できますが、WindowsではCTRL+BRKが機能します。VisualVM(これもJDKにあります)などの他のプロファイリングツールが役立ちます。最後に、「同時マルチスレッドJavaプログラムの潜在的なデッドロックを見つけるためのオープンソースツール」であるJCarderがあります。
スレッドに明示的なロックを共有させることができます (java.util.concurrent.lock.Lock を参照)。次に、オプションのタイムアウトを取ることができる Lock.tryLock() を使用できます。
また、Java 1.6 (1.5 については不明) に付属の jstack ユーティリティを使用することもできます。これは、すべてのスレッドの状態と、待機中または待機中でない可能性があるものを出力します。プロセスIDで呼び出すだけです。例えば。:
> jstack PID
"Signal Dispatcher" daemon prio=10 tid=0x00000000408e8400 nid=0x79a8 runnable [0x0000000000000000..0x000000004143f810]
java.lang.Thread.State: RUNNABLE
"Finalizer" daemon prio=10 tid=0x00000000408c9400 nid=0x79a7 in Object.wait() [0x0000000041a7b000..0x0000000041a7bb00]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00007f992d1e7050> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:116)
- locked <0x00007f992d1e7050> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:132)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:159)
"Reference Handler" daemon prio=10 tid=0x00000000408c2000 nid=0x79a6 in Object.wait() [0x000000004197a000..0x000000004197ac80]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x00007f992d41a958> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:485)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:116)
- locked <0x00007f992d41a958> (a java.lang.ref.Reference$Lock)
従来の同期メソッドではタイムアウトを使用できません。ただし、「新しい」java.util.concurrentのものを使用すると、タイムアウトをサポートするプログラムロックを使用できます。
たとえば、java.util.concurrent.locks.ReentrantLockを見てください
同期されたメソッドとステートメントを使用してアクセスされる暗黙のモニターロックと同じ基本的な動作とセマンティクスを備えた、再入可能な相互排除ロックですが、拡張機能を備えています。
2 つの理由が考えられます。1) スレッドが停止した。2) スレッドがどこかでロックされているか、予期しないことを行っている。
最良の解決策は、常にデバッガーを使用する (状況が発生するまで待ってからアプリケーションを一時停止する) か、JConsole/JStack/JVisualVM を使用することです。
同期されたメソッドのロックを待機している間にタイムアウトが発生する可能性はありますが、これらを実装するのは扱いにくいです。基本的に、T秒後にブロックスレッドを中断するタイマースレッドを生成します...良くありません。
Java 5 以降を使用している場合は、新しい並行性クラスが提供するものを確認することを強くお勧めします。たとえば、メソッドtryLock(long timeout, TimeUnit unit)を持つReentrantLockの使用を検討できます。これにより、ロックの取得を試みることができますが、一定時間後にエスケープすることができます。
Java Management Extension (JMX) API を使用して、Java のデッドロックを把握できます。例については、http://ourownjava.com/how-to-identify-deadlock-in-javaをご覧ください。