どうすればプログラムで Javaプログラムでデッドロックが発生したことを検出できますか?
JDKに付属のThreadMXBean
を使用して、プログラムでこれを行うことができます。
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long[] threadIds = bean.findDeadlockedThreads(); // Returns null if no threads are deadlocked.
if (threadIds != null) {
ThreadInfo[] infos = bean.getThreadInfo(threadIds);
for (ThreadInfo info : infos) {
StackTraceElement[] stack = info.getStackTrace();
// Log or store stack trace information.
}
}
明らかに、このデッドロックチェックを実行しているスレッドを分離しようとする必要があります。
ちなみに、これはJConsoleが内部で使用しているものです。
調査のための1つの有用なヒント:
赤い手でアプリケーションをキャッチし、デッドロックが発生した疑いがある場合は、Java.exeコンソールウィンドウで「Ctrl-Break」(またはSolaris/Linuxでは「Ctrl- \」)を押します。 jvmは、すべてのスレッドの現在のステータスとスタックトレースをダンプし、デッドロックを見つけ、それらを正確に記述します。
次のようになります。
Full thread dump Java HotSpot(TM) Client VM (1.5.0_09-b03 mixed mode):
"[Test Timer] Request Queue" prio=6 tid=0x13d708d0 nid=0x1ec in Object.
wait() [0x1b00f000..0x1b00fb68]
at Java.lang.Object.wait(Native Method)
at Java.lang.Object.wait(Unknown Source)
at library.util.AsyncQueue.run(AsyncQueue.Java:138)
- locked <0x02e70000> (a test.server.scheduler.SchedulerRequestQueue)
...
Found one Java-level deadlock:
=============================
"Corba service":
waiting to lock monitor 0x13c06684 (object 0x04697d90, a Java.lang.Object),
which is held by "[Server Connection] Heartbeat Timer"
"[Server Connection] Heartbeat Timer":
waiting to lock monitor 0x13c065c4 (object 0x0467e728, a test.proxy.ServerProxy), which is held by "Corba service"
Java stack information for the threads listed above:
===================================================
"Corba service":
at test.proxy.ServerProxy.stopHBWatchDog(ServerProxy:695)
- waiting to lock <0x04697d90> (a Java.lang.Object)
...
ThreadMXBeanクラスを使用して、プログラムでデッドロックスレッドを検出できます。次のコードは、
ThreadMXBean bean = ManagementFactory.getThreadMXBean();
long ids[] = bean.findMonitorDeadlockedThreads();
if(ids != null)
{
ThreadInfo threadInfo[] = bean.getThreadInfo(ids);
for (ThreadInfo threadInfo1 : threadInfo)
{
System.out.println(threadInfo1.getThreadId()); //Prints the ID of deadlocked thread
System.out.println(threadInfo1.getThreadName()); //Prints the name of deadlocked thread
System.out.println(threadInfo1.getLockName()); //Prints the string representation of an object for which thread has entered into deadlock.
System.out.println(threadInfo1.getLockOwnerId()); //Prints the ID of thread which currently owns the object lock
System.out.println(threadInfo1.getLockOwnerName()); //Prints name of the thread which currently owns the object lock.
}
}
else
{
System.out.println("No Deadlocked Threads");
}
デッドロックスレッドを検出する方法の詳細については、 here をクリックしてください。
JArmus は、デッドロックの検出と回避のためのライブラリです。次のサポートが含まれています:_Thread.join
_、CyclicBarrier
、CountDownLatch
、Phaser
、およびReentrantLock
。
JArmusを使用するには、コードをインスツルメントする必要があります。インスツルメントされたクラスの1つを使用するか、JArmusインストゥルメントjarmusc
を使用して自動的に。
_Java -jar jarmusc.jar yourprogram.jar checkedprogram.jar
_
入力_yourprogram.jar
_は、チェックするプログラムです。出力は、デッドロックを自動的に検出するためのチェック付きの同じプログラムです。
クラスCyclicBarrier
、CountDownLatch
、Phaser
を使用したデッドロックの検証は少し注意が必要です。たとえば、JConsoleはこれらのタイプのデッドロックを検出できません。 JArmusには少し助けが必要です。同期に影響を与えるスレッドを指定する必要があります。これらのスレッドをregisteredスレッドと呼びます。
できるだけ早く、スレッドは自分自身を登録済みとしてマークする必要があります。登録済みのスレッドをマークする適切な場所は、開始メソッド_Runnable.run
_です。 JArmus.register(latch);
デッドロックする次のプログラムは、JArmusによって正しく識別されます。
_final CountDownLatch latch = new CountDownLatch(2);
final CyclicBarrier barrier = new CyclicBarrier(2);
final Queue<Exception> exceptions = new ArrayDeque<>();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
JArmus.register(barrier); // do not forget to register!
JArmus.register(latch); // do not forget to register!
latch.countDown();
latch.await();
barrier.await();
} catch (Exception e) {
exceptions.add(e);
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
JArmus.register(barrier); // do not forget to register!
JArmus.register(latch); // do not forget to register!
barrier.await();
latch.countDown();
latch.await();
} catch (Exception e) {
exceptions.add(e);
}
}
});
t1.start();
t2.start();
_
IBMのMTRAT を検討することもできます。結局、予防は治療よりも優れています。 マルチコアソフトウェア開発キット には、デッドロック検出ツールも付属しています。
プログラムによる検出が必要ない場合は、JConsole;を使用してこれを実行できます。スレッドタブには「デッドロックの検出」ボタンがあります。 JDK6では、これは組み込みモニターとj.u.c
Lock
sの両方のロックを検出します
$Java_HOM/bin/jconsole
コマンドを使用してJConsoleを実行します
tempus-fugit は、プログラムスレッドダンプクラスとともに実装します。上記のmbeanメカニズムを使用して実装されており、すぐに使用できるスーパードゥーパーソリューションを提供します。
魔法はThreadMonitor.findDeadlock()
で発生します:
_ public boolean findDeadlock() {
long[] tids;
if (findDeadlocksMethodName.equals("findDeadlockedThreads")
&& tmbean.isSynchronizerUsageSupported()) {
tids = tmbean.findDeadlockedThreads();
if (tids == null) {
return false;
}
System.out.println("Deadlock found :-");
ThreadInfo[] infos = tmbean.getThreadInfo(tids, true, true);
for (ThreadInfo ti : infos) {
printThreadInfo(ti);
printLockInfo(ti.getLockedSynchronizers());
System.out.println();
}
} else {
tids = tmbean.findMonitorDeadlockedThreads();
if (tids == null) {
return false;
}
ThreadInfo[] infos = tmbean.getThreadInfo(tids, Integer.MAX_VALUE);
for (ThreadInfo ti : infos) {
// print thread information
printThreadInfo(ti);
}
}
return true;
}
_
これは、Java 5および6(したがって、外側のif()
)に異なる名前を持つThreadMXBean
のAPIを呼び出します。
このコード例では、ロックを中断することもできるため、デッドロックを解除することもできます。
実行時に実行したい場合は、 watchdog を使用できます。