我正在努力解决这样一个问题:在一天的大部分时间里顺利工作之后,一个可调用的任务被放入Java单线程执行器中,显然永远不会被执行。提交新任务的后续调用失败,ExecutorService
似乎已经死亡。此时,生成任务的客户端将停止服务,直到可以重新启动该流程,这在工作时间是不可能的。
一些背景:
多个高吞吐量生产者线程将其任务放在他们自己的专用Single Thread ExecutorService
上并立即返回。低延迟对于生产者线程非常重要。生成器线程和执行程序线程之间存在一对一的关系。需要为每个生产者线程处理任务。任务可以在执行程序线程中排队,并在需要执行时执行。交通突发,所以消费者总是赶上他们的生产者。
JDK:RedHat Linux上的jdk1.8.0_92
我定义了我的执行者服务:
private final ExecutorService inboundMsgSender = Executors.newSingleThreadExecutor();
生产者线程调用回调:
public void onMessageFromFix(MessageEvent event, final Message message) {
log.info("submit to Executor: " + message.toString());
inboundMsgSender.submit(new Callable<Void>() {
public Void call() {
try {
onMessageFromExecutor(event, message);
} catch (Throwable e) {
log.error("error", e);
}
return null;
}
});
}
ExecutorService调用callable:
public void onMessageFromExecutor(MessageEvent event, final Message message) {
try {
log.info("call from Executor: " + message.toString());
doExpensiveLogic(message);
} catch (Exception e) {
log.error("error", e);
}
}
在正常情况下我会在日志文件中看到:
submit to Executor: 4928
call from Executor: 4928
这就是我知道Executor线程正在运行Callable的方法。
问题发生时,我只看到以下内容:
submit to Executor: 4928
没有后续call from Executor
且没有例外。
答案 0 :(得分:0)
永远不会执行可调用任务的原因是因为inboundMsgSender Single Thread ExecutorService
内的线程被阻塞,等待来自前一次调用的`doExpensiveLogic(消息)内的FutureTask.get()
。
这里的教训是我假设ExecutorService的线程在被阻止时正在死亡。线程死亡由ExecutorService处理,所以我等待问题再次发生,我使用JStack进行了线程转储。线程转储显示了执行程序服务的线程被阻止的确切位置。
"pool-54-thread-1" #354 prio=5 os_prio=0 tid=0x567c3c00 nid=0xae4a waiting on condition [0x51125000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x69458368> (a com.aqua.api.SequentialExecutorService$ClientTaskHandle)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.FutureTask.awaitDone(FutureTask.java:429)
at java.util.concurrent.FutureTask.get(FutureTask.java:191)
at com.aqua.jms.multiserver.impl.MultiServerJmsConnection.isConsumerConfigured(MultiServerJmsConnection.java:301)
at com.aqua.jms.multiserver.migration.MigrationConnectionWrapper.getAdministrationConnection(MigrationConnectionWrapper.java:152)
我再次发生的步骤:
你可以从堆栈跟踪中清楚地看到线程是在FutureTask.get()上的LIVE和WAITING所以需要做的就是修复Future Task或者重构逻辑并使其可用于我的线程直接打电话。