一个线程池的多个 CompletionService Java
Multiple CompletionService for one thread pool Java
我正在开发具有以下一般架构的 Java 服务器应用程序:
- 客户端向服务器发出 RPC 请求
- 我相信 RPC 服务器 (gRPC) 有自己的线程池来处理请求
- 请求会立即插入
Thread Pool 1
以进行更多处理
- 一个具体的请求类型,我们称之为
Request R
,需要运行几个异步任务并行,判断结果形成共识,才会return客户端。这些任务有点长运行ning,所以我用一个单独的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
中可能 运行ning 的任何其他内容严格隔离。
- 我的问题:
- 首先,Java的
CompletionService
是否隔离?在检查 JavaDocs 后,我找不到关于此的良好文档。换句话说,如果两个或更多 CompletionService
由 相同的 线程池支持,那么它们中的任何一个是否有拉动属于另一个 [=21= 的未来的风险]?
- 其次,为每个请求创建这么多
CompletionService
是一种不好的做法吗?有没有更好的方法来处理这个问题?当然,为每个请求创建一个新的线程池是个坏主意,所以有没有更多的 canonical/correct 方法来隔离 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();
}
}
Firstly, is Java's CompletionService
isolated?
这不是保证,因为它是一个接口,因此实现决定了这一点。但由于唯一的实现是 ExecutorCompletionService
我只想说答案是:是的。 ExecutorCompletionService
的每个实例在内部都有一个 BlockingQueue
,已完成的任务在其中排队。实际上,当您在服务上调用 take
时,它只是通过调用 take
将调用传递到队列。每个提交的任务都由另一个对象包装,完成后将任务放入队列中。因此每个实例管理它提交的与其他实例隔离的任务。
Secondly, is this bad practice to be creating this many CompletionService
s for each request?
我会说没关系。 CompletionService
只不过是执行程序的一个相当薄的包装器。您必须忍受“开销”(任务的内部 BlockingQueue
和包装器实例),但它很小,您从中获得的收益可能远远超过成本。有人可能会问您是否只需要一个来完成 2 到 3 个任务,但这在某种程度上取决于任务。在这一点上,问题是 CompletionService
一般而言是否值得,所以这由您决定,因为它超出了您的问题范围。
我正在开发具有以下一般架构的 Java 服务器应用程序:
- 客户端向服务器发出 RPC 请求
- 我相信 RPC 服务器 (gRPC) 有自己的线程池来处理请求
- 请求会立即插入
Thread Pool 1
以进行更多处理 - 一个具体的请求类型,我们称之为
Request R
,需要运行几个异步任务并行,判断结果形成共识,才会return客户端。这些任务有点长运行ning,所以我用一个单独的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
中可能 运行ning 的任何其他内容严格隔离。 - 我的问题:
- 首先,Java的
CompletionService
是否隔离?在检查 JavaDocs 后,我找不到关于此的良好文档。换句话说,如果两个或更多CompletionService
由 相同的 线程池支持,那么它们中的任何一个是否有拉动属于另一个 [=21= 的未来的风险]? - 其次,为每个请求创建这么多
CompletionService
是一种不好的做法吗?有没有更好的方法来处理这个问题?当然,为每个请求创建一个新的线程池是个坏主意,所以有没有更多的 canonical/correct 方法来隔离CompletionService
中的未来,或者我正在做的事情好吗?
- 首先,Java的
在此先感谢您的帮助。非常感谢任何指向有用文档或示例的指针。
代码,供参考,虽然琐碎:
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();
}
}
Firstly, is Java's
CompletionService
isolated?
这不是保证,因为它是一个接口,因此实现决定了这一点。但由于唯一的实现是 ExecutorCompletionService
我只想说答案是:是的。 ExecutorCompletionService
的每个实例在内部都有一个 BlockingQueue
,已完成的任务在其中排队。实际上,当您在服务上调用 take
时,它只是通过调用 take
将调用传递到队列。每个提交的任务都由另一个对象包装,完成后将任务放入队列中。因此每个实例管理它提交的与其他实例隔离的任务。
Secondly, is this bad practice to be creating this many
CompletionService
s for each request?
我会说没关系。 CompletionService
只不过是执行程序的一个相当薄的包装器。您必须忍受“开销”(任务的内部 BlockingQueue
和包装器实例),但它很小,您从中获得的收益可能远远超过成本。有人可能会问您是否只需要一个来完成 2 到 3 个任务,但这在某种程度上取决于任务。在这一点上,问题是 CompletionService
一般而言是否值得,所以这由您决定,因为它超出了您的问题范围。