我正在调试一个运行多个线程的 Java 应用程序。看了一会儿日志后,似乎其中一个线程不再运行。我的猜测是线程正在等待一个永远不会释放的锁(最后一个输出是在调用同步方法之前)。
我可以为线程配置超时吗?一种“等待这个锁,但如果它在 10 秒后不可用,请不要再等待!”</p>
我正在调试一个运行多个线程的 Java 应用程序。看了一会儿日志后,似乎其中一个线程不再运行。我的猜测是线程正在等待一个永远不会释放的锁(最后一个输出是在调用同步方法之前)。
我可以为线程配置超时吗?一种“等待这个锁,但如果它在 10 秒后不可用,请不要再等待!”</p>
您可以使用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 附带)之类的东西,其中包括一个名为“检测死锁”的按钮(至少它在 Java 6 中可以,我认为它在 Java 5 中不起作用)。另一种选择是向控制台生成线程转储 - 在 Unix 上,您可以键入“kill -3”,而在 Windows 上 CTRL+BRK 将起作用。其他分析工具,例如 VisualVM(也在 JDK 中)可以提供帮助。最后还有JCarder,它是“一个用于在并发多线程 Java 程序中发现潜在死锁的开源工具”。
您可以让线程共享一个显式锁(请参阅 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 或更高版本,我强烈建议您查看新的并发类提供的内容。例如,您可以考虑使用ReentrantLock,它有一个tryLock(long timeout, TimeUnit unit)方法,它允许您尝试获取锁,但允许您在固定时间后逃脱。
您可以使用 Java 管理扩展 (JMX) API 来找出 Java 中的死锁。查看http://ourownjava.com/how-to-identify-deadlock-in-java以获取示例。