如何检测死锁? 同步块超时?

我正在调试运行多个线程的Java应用程序。 经过一段时间观察日志后,似乎其中一个线程不再运行了。 我的猜测是线程正在等待一个永不释放的锁(最后一个输出是在调用synchronized方法之前)。

我可以为线程配置超时; 一种“等待锁定,但如果在10秒后不能使用,请不要再等了!”

您可以使用java.util.concurrent.Lock而不是内部Object锁。 没有公平排序的RentrantLock具有与内在锁相同的基本行为和语义。 有一个方法tryLock采用超时参数:

 Lock lock = ...; if (lock.tryLock(10L, TimeUnit.SECONDS)) { try { // manipulate protected state } finally { lock.unlock(); } } else { // perform alternative actions } 

您可以使用调试工具或分析器,而不是添加额外的代码进行调试。

一种选择是使用类似JConsole(随JDK一起提供)的东西,其中包括一个名为“Detect Deadlock”的按钮(至少它在Java 6中有用,我认为它不适用于Java 5)。 另一个选择是生成一个线程转储到控制台 – 在Unix上你可以键入“kill -3”,而在Windows上CTRL + BRK将起作用。 其他分析工具(如VisualVM(也在JDK中))可以提供帮助。 最后是JCarder ,它是“用于在并发multithreadingJava程序中查找潜在死锁的开源工具”。

您可以让线程共享一个显式锁(请参阅java.util.concurrent.lock.Lock)。 然后,您可以使用Lock.tryLock(),它可以采取可选的超时。

您还可以使用java 1.6附带的jstack实用程序(不确定1.5),它将打印出所有线程的状态以及它们可能或不可能等待的内容。 只需使用进程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) 

您不能使用传统同步方法的超时。 但是,使用“new”java.util.concurrent东西,您可以使用具有超时支持的程序锁。

例如,查看java.util.concurrent.locks.ReentrantLock

可重入互斥锁具有与使用同步方法和语句访问的隐式监视器锁相同的基本行为和语义,但具有扩展function。

可能有两个原因:1)线程死亡2)线程被锁定在某处或做了一些你没想到的事情。

最好的解决方案是始终使用调试器(等到情况发生然后暂停应用程序)或使用JConsole / JStack / JVisualVM。

虽然在等待同步方法的锁定时可能会有超时,但实现这些方法并不实用。 基本上你会产生一个计时器线程,它会在T秒后中断块线程……不太好。

如果您使用的是Java 5或更高版本,我强烈建议您查看新的并发类提供的内容。 例如,您可以考虑使用ReentrantLock ,它有一个方法tryLock(long timeout,TimeUnit unit) ,它允许您尝试获取锁定但允许您在固定的时间后转义。

您可以使用Java Management Extension(JMX)API来确定java中的死锁。 查看http://ourownjava.com/how-to-identify-deadlock-in-java以获取示例。