多个调度线程提交任务RejectedExecutionException

时间:2017-10-24 15:45:22

标签: java multithreading concurrency thread-safety executorservice

在我的应用程序中,我有多个创建任务的scheduler个线程。例如每个调度程序线程可以创建一堆任务:

TaskCreator tastCreator;
for (Report report: report) {
     taskCreator.createTask(report);
}

调度程序线程可以从日志中看到并发运行:

15:57:20.107  INFO [    scheduler-4] c.task.ReportExportSchedulerTask   : Task created
15:57:20.107  INFO [    scheduler-2] c.task.ReportExportSchedulerTask   : Task created

我有一个TaskCreator组件,如下所示,将任务传递给executeJob()

@Component
public class TaskCreator {
    @Autowired
    private SftpTaskExecutor sftpTaskExecutor;
    @Autowired
    SftpConfig sftpConfig;

    @Autowired
    private SFTPConnectionManager connectionManager;

    public void createTask(Report report) {
        sftpTaskExecutor.executeJob(new JobProcessorTask(...));
    }

    public void validateTasksExecution() {
        sftpTaskExecutor.getExecutorService().shutdown();
        while (!sftpTaskExecutor.getExecutorService().isTerminated()) ;
        connectionManager.disconnect();
    }
}

SftpTaskExecutor Component如下构建executorService,我将上述任务提交给:{/ p>

@Component
public class SftpTaskExecutor {
    private ExecutorService executorService = Executors.newSingleThreadExecutor();

    public void executeJob(JobProcessorTask jobProcessorTask) {
        executorService.execute(jobProcessorTask);
    }

    public ExecutorService getExecutorService() {
        return executorService;
    }
}

我的问题是,如果两个或多个调度程序线程正在创建任务并同时提交到执行程序服务,则上面抛出RejectedExecutionException,其中一个调度程序任务未完成(即文件未发送)

对于每个计划线程,我需要能够在不干扰其他调度程序线程的情况下调用validateTasksExecution()。换句话说,当其他调度程序仍在处理时,不要断开连接。

我在这方面是否正确使用ExecutorService?如何更改上面的线程安全?

1 个答案:

答案 0 :(得分:0)

  

我的问题是,如果两个或多个调度程序线程正在创建任务并同时提交到执行程序服务,则上面会抛出一个RejectedExecutionException,其中一个调度程序任务未完成(即文件未发送)

我们来看看javadocs for ExecutorService.execute(...)

  

RejectedExecutionException - 如果无法接受执行此任务。

在查看ThreadPoolExecutor(和相关联的)代码时,作业被拒绝有两个原因:

  • 作业队列已满(这不适用于您,因为队列默认无限制)
  • 执行程序服务不再运行(ding ding ding)

我相信您的执行程序服务已经关闭,很可能是因为第一个线程在第二个线程调用validateTasksExecution()之前调用了executeJob(...)。如果您尝试重用该线程池,则代码不正确。您还要关闭connectionManager(),这让我想知道您是否想要重新使用SftpTaskExecutor

如果您希望每个线程查看其操作是否已完成但让线程池保持运行,那么您需要从ExecutorService.submit(...)方法保存Future(s)并致电get()在他们。这将告诉你什么时候完成工作。

类似的东西:

public Future<Void> createTask(Report report) {
    return sftpTaskExecutor.executeJob(new JobProcessorTask(...));
}

public void validateTasksExecution(Future<Void> future) {
    // there is some exceptions here you need to handle
    future.get();
}

public void shutdown() {
    sftpTaskExecutor.shutdown();
    connectionManager.disconnect();
}

...

public Future<Void> executeJob(JobProcessorTask jobProcessorTask) {
    return executorService.submit(jobProcessorTask);
}

如果您需要监控多个作业,那么您应该将它们存储在一个集合中并连续调用它们get(),尽管这些作业将并行运行。

另一种选择是让你为每笔交易单独ExecutorService这是浪费但可能不是那么糟糕,因为它正在管理sftp调用。

while (!sftpTaskExecutor.getExecutorService().isTerminated()) ;
是的,你不想那样旋转。请参阅awaitTermination(...) javadocs