java - 单线程 Executor 默默的丢任务

标签 java multithreading

我正在努力解决一个问题,在一天中的大部分时间都顺利工作后,一个可调用的任务被放入 Java 单线程执行器中,但显然永远不会被执行。提交新任务的后续调用失败并且 ExecutorService似乎已经死了。此时,生成任务的客户端停止服务,直到流程可以重新启动,这在工作时间内是不可能的。

一些背景:
多个高吞吐量生产者线程将它们的任务放到自己专用的 Single Thread ExecutorService 上并立即返回。低延迟对于生产者线程非常重要。生产者线程和执行者线程之间存在一对一的关系。任务需要按顺序为每个生产者线程处理。任务可以在执行程序线程中排队,并在需要执行时花费时间。流量是突发的,所以消费者总是 catch 他们的生产者。

JDK:在 RedHat Linux 上为 jdk1.8.0_92

我定义了我的 Executor 服务:
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 调用可调用对象:
    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: 4928call from Executor: 4928
这就是我知道 Executor 线程正在运行 Callable 的方式。

出现问题时,我只看到以下内容:
submit to Executor: 4928
没有后续 call from Executor并且没有异常(exception)。

最佳答案

可调用任务从未执行的原因是因为 inboundMsgSender Single Thread ExecutorService 内部的线程被阻塞等待 FutureTask.get()在上一次调用的 `doExpensiveLogic(message) 中。

这里的教训是,我假设 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)

当它再次发生时我采取的步骤:
  • 标识执行器服务的单线程的线程名称。
  • 在 linux 上,识别进程的 PID。
  • 使用 jstack 获取 PID 的线程转储
    $ jstack 33516 > threaddump.txt
  • 在线程转储中搜索线程名称(见上文)。

  • 您可以从堆栈跟踪中清楚地看到线程在 FutureTask.get() 上处于 Activity 状态并在等待,因此所有需要做的就是修复 future 任务或重构其中的逻辑并使其可用于我的线程直接打电话。

    关于java - 单线程 Executor 默默的丢任务,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42236229/

    相关文章:

    java - 如何同时多次运行 Java 应用程序?

    c - 关于Getcontext函数的问题

    multithreading - 写入单个日志文件的多线程中的Powershell代码,但是由于文件正在使用中而丢失了一些数据

    c# - 审批工作流的同义词

    java - 如何使用 sdk 27​​ 在 android studio 中显示联系人电话列表

    java - 当 Java 系统检测到异常被抛出时会发生什么?

    java - 如何同步传递到多个地方的实例?

    c++ - 静态成员的两个实例,怎么可能?

    java - 在房间版本 1.1.1 中创建复合主键时无法使用 Kotlin 编译器构建 android studio 项目

    java - 什么取代了方法克隆(可序列化)