java - 为分配给线程池 java 的一组任务创建超时

标签 java multithreading executorservice

<分区>

您好,我遇到了有关超时分配给线程池 java 的任务的问题。

详细说明:

  1. 我已经实现了一个 API,它可以并行运行一些查询并返回响应。
  2. 有一个通过 Executors.newFixedThreadPool(40) 创建的固定线程池。每当有人调用此 API 时,一组 10 个任务就会在此线程池上进行调度。这些任务在内部对 Mysql 执行一组查询。
  3. 我必须以 6-7 秒的 SLA 返回 API 的响应。所以我必须为线程池上安排的所有 10 个任务创建一个超时。我知道如何使单个任务超时(它抛出一个中断的异常,尽管线程只有在它启动后完成时才会变得空闲)。
  4. 我也不想让线程池重载。因此,我为通过分配给该线程池的任务运行的所有查询创建了 60 秒的超时。因此,线程可以在一分钟内自由地执行另一项任务。

问题:

有没有优雅的方法来解决这个问题?

@Component
public class MyHandler {
    @PostConstruct
    public void init() {

        /*
        * Naming thread pool to identify threads in the thread dump
        * */

        ThreadFactory threadFactory = new ThreadFactoryBuilder()
            .setNameFormat("my-thread-%d").build();

        executorService = Executors.newFixedThreadPool("40", threadFactory);
    }

    @PreDestroy
    public void destroy() {
        executorService.shutdown();
    }

    public void update() {
        List<Future<Boolean>> results = new ArrayList<>();
        results.add(executorService.submit(new Callable<Boolean>() {
            @Override
            public Boolean call() throws Exception {
                executeQuery();
                return true;
            }
        }));

        /*
        * 9 more such tasks
        */


         for (Future<Boolean> result : results) {
            try {
                result.get();
            } catch (InterruptedException | ExecutionException e) {
                LOGGER.error("Failed with unknown error", e);
            }
        }
    }
}

executeQuery() 的超时时间为 60 秒。

最佳答案

您可以使用 ExecutorService.invokeAll() 运行一组超时任务。方法完成后(完成工作或超时),您将必须检查所有 future 以查看它们是否被取消(由于超时)或完成。如果它们已完成,您将必须检查它们是否因为工作已完成而完成,而不是因为异常(当您调用 Future.get 时)。

代码可能是这样的:

    final ExecutorService service = Executors.newCachedThreadPool();
    final List<Future<Double>> futures = service.invokeAll(tasks, 2, TimeUnit.SECONDS);
    final List<CallableTask> tasks = Arrays.asList(new CallableTask(1, TimeUnit.SECONDS),
            new CallableTask(1, TimeUnit.HOURS), new CallableTask(100, TimeUnit.MILLISECONDS),
            new CallableTask(50, TimeUnit.SECONDS));

    for (Future<Double> result : futures) {
        if (!result.isCancelled())  {
            try {
                System.out.println("Result: " + result.get());
            } catch (ExecutionException e) {
                // Task wasn't completed because of exception, may be required to handle this case
            }
        }
    }

在我的例子中,CallableTask 是一个 Callable 实现,它用于使代码更简单,因为提交的所有任务都是相同的。您可以使用相同的方法来简化您的代码。

我添加了 CallableTask 的示例:

    public class CallableTask implements Callable<Double> {

    private static AtomicInteger count = new AtomicInteger(0);
    private final int timeout;
    private final TimeUnit timeUnit;
    private final int taskNumber = count.incrementAndGet();

    public CallableTask(int timeout, TimeUnit timeUnit) {
        this.timeout = timeout;
        this.timeUnit = timeUnit;
    }

    @Override
    public Double call() {
        System.out.println("Starting task " + taskNumber);
        try {
            timeUnit.sleep(timeout);
        } catch (InterruptedException e) {
            System.out.println("Task interrupted: " + taskNumber);
            Thread.currentThread().interrupt();
            return null;
        }
        System.out.println("Ending task " + taskNumber);
        return Math.random();
    }
}

关于java - 为分配给线程池 java 的一组任务创建超时,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45136945/

相关文章:

java - 将库添加到 jar 中

java - 使用线程分割一个大文件

java - 主线程完成时优雅地关闭 ExecutorService

hibernate - Grails 和后台任务

java - java死代码警告

java - Spring Data MongoDB - 始终使用默认参数连接

java - if else block 的替代方案 - 检查数字是否在 2 个数字之间

java - 多线程java程序交替打印偶数和奇数

java - 从不同线程更新 UI

java - 每个线程上有多个任务的执行器服务