使用 ExecutorService 的时间限制任务

Time bound tasks using ExectutorService

我早就运行ning任务提交了一个ExecutorService。该任务可以 运行 相当长一段时间。同时,新的任务被提交到内部阻塞队列。

提交的任务完成后,会发回通知以从队列中释放任务以供执行。但是,有时,由于编程错误或网络问题,通知不会被触发。在这种情况下,我的任务队列有可能会变得非常大,我可能会遇到任务可能永远躺在队列中的情况。

为了克服这个问题,我正在考虑编写一个线程,它会定期检查任务在队列中空闲的时间。如果任务在队列中等待了 15 分钟,我会假设较早提交的任务遇到了错误,因此没有 return。然后我将从队列中逐出任务并允许它执行。

是否有任何现有机制来处理这个问题,或者我将不得不编写这个自定义逻辑?

注:
我不喜欢 ScheduledExecutor 服务的原因是因为并非所有任务都需要定期执行。只有失败的场景应该在一定的延迟后执行。

编辑 架构简要概述 我正在设计的解决方案应该支持许多并发的静态文件下载。通常可能有数千个下载请求。下载请求由基于 UI 的应用程序触发。这样我就知道什么时候会触发请求。利用这种方法,我打算限制下载请求。

如果用户创建了 300 个下载请求,会发生什么情况?

  1. 应用程序工作线程创建 300 个下载任务
  2. 100 个任务是 submitted.I 定义的最大 HTTP 线程池大小为 100。这意味着我可以支持最多 100 个同步并行下载(servlet 2.5)任务反过来要求远程 HTTP 客户端执行 HTTP 获取。请注意,HTTP 线程尚未发挥作用
  3. 剩余 200 个请求正在排队。
  4. HTTP 客户端执行 HTTP Get。 HTTP 线程现在以阻塞方式流式传输响应。
  5. 收到 200 OK 后,我会创建一个通知,通知其中一个客户端已完成下载。
  6. 限制现在 release/submit 之前排队的 200 个请求中的任务之一。

在我能够收到响应(HTTP 200/HTTP 500 等)的情况下,节流机制非常有效。但是,例如,如果 servlet 本身抛出异常,我没有收到任何响应以指示 HTTP 工作线程空闲。因此任务有可能永远保留在队列中。为了克服这个问题,我在考虑一种基于计时器的方法,如果 15 分钟内没有 HTTP 响应,则提交下一个队列任务以供执行。一种避免重大内存泄漏的回退机制。

通过超时调用 get() 来限制任务允许的最长时间,并在捕获 TimeoutException 时执行清理。

这是一个在等待时不会阻塞主线程的实现:

ExecutorService executor = Executors.newCachedThreadPool();
ExecutorService monitor = Executors.newFixedThreadPool(99);

public void submit(Runnable task) {
    Runnable monitorTask = new Runnable() {
        @Override
        public void run() {
            Future<?> future = executor.submit(task);
            try {
                future.get(15, TimeUnit.MINUTES);
            } catch (InterruptedException e) {
                // retry waiting. iterative approach not shown here
            } catch (ExecutionException e) {
                // your task exploded
            } catch (TimeoutException e) {
                // your task timed out - perform clean up, eg
                future.cancel(true);
            }               
        }
    };
    monitor.submit(monitorTask);
}

单独的线程池用于防止没有线程可用于监视,但线程可用于执行导致未监视任务的任务。