【发布时间】:2014-10-23 00:41:26
【问题描述】:
请注意:我将其标记为 JClouds,因为如果您阅读了整个问题和随之而来的 cmets,我认为这要么是 JClouds 的错误,要么是对该库的滥用。
我有一个可执行的 JAR,它可以运行,工作一段时间,完成工作而不抛出任何错误/异常,然后在它应该退出时永远挂起。我使用 VisualVM 对其进行了分析(注意正在运行的线程),并且我还在应用程序挂起的位置(在 main() 方法的末尾)添加了一条日志语句以打印。这是我的主要方法的最后一部分:
Set<Thread> threadSet = Thread.getAllStackTraces().keySet();
for(Thread t : threadSet) {
String daemon = (t.isDaemon()? "Yes" : "No");
System.out.println("The ${t.getName()} thread is currently running; is it a daemon? ${daemon}.");
}
当我的 JAR 执行此代码时,我看到以下输出:
The com.google.inject.internal.util.Finalizer thread is currently running; is it a daemon? Yes.
The Signal Dispatcher thread is currently running; is it a daemon? Yes.
The RMI Scheduler(0) thread is currently running; is it a daemon? Yes.
The Attach Listener thread is currently running; is it a daemon? Yes.
The user thread 3 thread is currently running; is it a daemon? No.
The Finalizer thread is currently running; is it a daemon? Yes.
The RMI TCP Accept-0 thread is currently running; is it a daemon? Yes.
The main thread is currently running; is it a daemon? No.
The RMI TCP Connection(1)-10.10.99.8 thread is currently running; is it a daemon? Yes.
The Reference Handler thread is currently running; is it a daemon? Yes.
The JMX server connection timeout 24 thread is currently running; is it a daemon? Yes.
我不认为我必须担心守护进程(如果我错了,请纠正我),因此将其过滤到非守护进程:
The user thread 3 thread is currently running; is it a daemon? No.
The main thread is currently running; is it a daemon? No.
显然,主线程仍在运行,因为某些东西阻止它退出。嗯,user thread 3 看起来很有趣。 VisualVM 告诉我们什么?
这是应用程序挂起时的线程视图(上面的控制台输出打印时发生的情况)。嗯user thread 3 看起来更可疑了!
所以在终止应用程序之前,我进行了线程转储。这是user thread 3 的堆栈跟踪:
"user thread 3" prio=6 tid=0x000000000dfd4000 nid=0x2360 waiting on condition [0x00000000114ff000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x0000000782cba410> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:186)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2043)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1068)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615)
at java.lang.Thread.run(Thread.java:744)
Locked ownable synchronizers:
- None
我以前从来没有分析过其中任何一个,所以这对我来说意味着胡言乱语(但对于受过训练的眼睛来说可能不是!)。
杀死应用程序后,VisualVM 的时间线停止每秒滴答/递增,我可以在时间线中水平向后滚动到 user thread 3 的创建位置,并开始它作为一个唠叨线程的生命:
但是我不知道如何判断代码user thread 3 的创建位置。所以我问:
- 我如何知道创建
user thread 3的内容以及创建位置(特别是因为我怀疑它是创建线程的第 3 方 OSS 库)? - 如何分类、诊断和修复此线程挂起?
更新:
这是我的代码,大约在同一时间,user thread 3 似乎正在创建:
ExecutorService myExecutor = Executors.newCachedThreadPool();
for(Node node : nodes) {
BootstrapAndKickTask bootAndKickTask = new BootstrapAndKickTask(node, ctx);
myExecutor.execute(bootAndKickTask);
}
myExecutor.shutdown();
if(!myExecutor.awaitTermination(15, TimeUnit.MINUTES)) {
TimeoutException toExc = new TimeoutException("Hung after the 15 minute timeout was reached.");
log.error(toExc);
throw toExc;
}
还有我的GitHub Gist,其中包含完整的线程转储。
【问题讨论】:
-
这看起来像是 Executor 的 ThreadPool 没有被正确关闭。您可能需要在某些组件上调用
shutdown、terminate、close方法。 -
甚至守护线程也参与死锁。只需在此处提供所有堆栈跟踪。
-
@Basilevs 可以。如果 ExecutorService 仍然可以访问,比如从全局变量中,finalize() 将永远不会被调用,也永远不会关闭。默认情况下,为 ExecutorService 创建的线程被创建为非守护进程。
-
@Zac docs.oracle.com/javase/7/docs/api/java/lang/…。不过不建议使用。对于您发布的内容,您正在关闭它。这很奇怪。我的猜测是正在创建另一个池。
-
@Zac 如果是的话。然后我认为这是创建池的地方。在文件中搜索“用户线程 %d”。 github.com/jclouds/jclouds/blob/…
标签: java multithreading concurrency deadlock jclouds