【问题标题】:Multiple CompletionService for one thread pool Java一个线程池Java的多个CompletionService
【发布时间】:2020-10-20 05:15:21
【问题描述】:

我正在开发具有以下一般架构的 Java 服务器应用程序:

  • 客户端向服务器发出 RPC 请求
  • 我相信 RPC 服务器 (gRPC) 有自己的线程池来处理请求
  • 请求会立即插入Thread Pool 1 以进行更多处理
  • 一个具体的请求类型,我们叫Request R,需要并行运行几个异步任务,判断结果形成共识,返回给客户端。这些任务运行时间较长,因此我使用单独的Thread Pool 2 来处理这些请求。重要的是,每个Request R 都需要运行相同的 2-3 个异步任务。 Thread Pool 2 因此服务于所有当前正在执行Request R 的服务。但是,Request R 应该只能查看和检索属于它的异步任务。
  • 为了实现这一点,在每个传入的Request R 中,当它在Thread Pool 1 中时,它将为请求创建一个新的CompletionService,由Thread Pool 2 支持。它将提交 2-3 个异步任务,并检索结果。这些应与 Thread Pool 2 中可能运行的属于其他请求的任何其他内容严格隔离。
  • 我的问题:
    • 首先,Java 的CompletionService 是隔离的吗?检查JavaDocs 后,我找不到关于此的好的文档。换句话说,如果两个或更多 CompletionServicesame 线程池支持,它们中的任何一个是否有可能将未来拉到另一个 CompletionService 上?
    • 其次,为每个请求创建这么多CompletionService 是不是不好?有没有更好的方法来处理这个?当然,为每个请求创建一个新的线程池是一个坏主意,那么有没有更规范/正确的方法来隔离 CompletionService 中的期货,或者我正在做的事情好吗?

提前感谢您的帮助。任何指向有用的文档或示例的指针将不胜感激。

代码,供参考,虽然琐碎:

public static final ExecutorService THREAD_POOL_2 =
        new ThreadPoolExecutor(16, 64, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>());

// Gets created to handle a RequestR, RequestRHandler is run in Thread Pool 1
public class RequestRHandler {

    CompletionService<String> cs;

    RequestRHandler() {
        cs = new ExecutorCompletionService<>(THREAD_POOL_2);
    }

    String execute() {
        cs.submit(asyncTask1);
        cs.submit(asyncTask2);
        cs.submit(asyncTask3);

       // Lets say asyncTask3 completes first
      Future<String> asyncTask3Result = cs.take();

      // asyncTask3 result indicates asyncTask1 & asyncTask2 results don't matter, cancel them
      // without checking result


      // Cancels all futures, I track all futures submitted within this request and cancel them,
      // so it shouldn't affect any other requests in the TP 2 pool
      cancelAllFutures(cs);


      return asyncTask3Result.get();  
    }


}

【问题讨论】:

    标签: java concurrency completion-service


    【解决方案1】:

    首先,Java 的 CompletionService 是孤立的吗?

    这不是保证,因为它是一个接口,所以实现决定了这一点。但由于唯一的实现是ExecutorCompletionService,我只想说答案是:是的。 ExecutorCompletionService 的每个实例在内部都有一个 BlockingQueue,已完成的任务在其中排队。实际上,当您在服务上调用take 时,它只是通过调用take 将调用传递给队列。每个提交的任务都由另一个对象包装,该对象在完成时将任务放入队列中。因此,每个实例管理其提交的任务,与其他实例隔离。

    其次,为每个请求创建这么多 CompletionServices 是不是一种不好的做法?

    我会说没关系。 CompletionService 只不过是执行器周围的一个相当薄的包装器。您必须忍受“开销”(内部BlockingQueue 和任务的包装器实例),但它很小,您从中获得的收益可能比成本要多。有人可能会问你是否只需要一个来完成 2 到 3 个任务,但这有点取决于任务。在这一点上,这是一个关于 CompletionService 是否值得的问题,所以这取决于您来决定,因为它超出了您的问题范围。

    【讨论】:

      猜你喜欢
      • 2021-05-09
      • 1970-01-01
      • 2012-09-03
      • 1970-01-01
      • 1970-01-01
      • 2016-05-15
      • 2011-09-23
      • 2016-10-18
      • 2019-07-07
      相关资源
      最近更新 更多