首页 > 解决方案 > 在服务中配置线程池大小

问题描述

我正在编写一个服务,它需要两个 urlurlAurlB获取两个整数ab. 该服务返回sum和。ab

以最简单的形式,该服务的工作方式如下:

public Integer getSumFromUrls(String urlA, String urlB) {

    Integer a = fetchFromUrl(urlA);
    Integer b = fetchFromUrl(urlB);

    return a + b;
}

fetchFromUrl是一个同步操作,因此它会阻塞处理线程,除非该值可用。为了使事情变得高效,我宁愿ExecutorService安排两次提取并在结果可用时返回。这是更改后的代码(忽略语法上的细微差别)

public Integer getSumFromUrls(String urlA, String urlB) {
    Future<Integer> aFuture = Executors.newSingleThreadScheduledExecutor().submit(new Callable<Integer>() {
        public Integer call() {
            return fetchFromUrl(urlA);
        }

    });
    Future<Integer> bFuture = Executors.newSingleThreadScheduledExecutor().submit(new Callable<Integer>() {
        public Integer call() {
            return fetchFromUrl(urlB);
        }                                                                                
    });

    Integer a = aFuture.get();
    Integer b = bFuture.get();

    return a + b;
}

在这里,我创建了单线程执行器来同时执行请求。

由于此代码将在 Web 服务的上下文中运行,我可能不应该在函数内部本地创建单线程执行程序,而是应该使用一些在请求之间共享的 N 大小的线程池。

我的问题是:

  1. 上述理解(斜体部分)是否正确?
  2. 如果是,我应该如何选择线程池的最佳大小。它应该是我的服务容器的线程池大小的函数,还是请求吞吐量或两者兼而有之?
  3. 有没有更好的方法来优化这种情况,以便服务线程在大多数情况下不会在执行 IO 时被阻塞。

注意:此问题中提供的详细信息并非完全真实的场景,而是代表回答问题所需的同一组复杂性。

标签: javamultithreadingconcurrencythreadpoolexecutorservice

解决方案


如果您的函数getSumFromUrls在每次新请求到来时都执行,这意味着它将每次创建一个新的线程池并提交任务。假设如果您1000在任何时间点都有请求命中,那么1000将创建 ThreadPool 并最终创建1000s线程。我相信如果您在任何时候创建 1000 或更多线程,这对您的应用程序来说将是一个问题。通常在任何时间点,活动线程的数量应该大约/等于available core系统大小的数量,但是这完全取决于用例假设你的任务是CPU intensive线程数应该是 CPU 核心大小但是如果你任务是IO intensive那么你可以有更多的线程数。更多数量的线程意味着将发生更多数量的上下文切换,这有其自身的成本并可能降低应用程序性能。

上述理解(斜体部分)是否正确?

-> 是的。

如果是,我应该如何选择线程池的最佳大小。它应该是我的服务容器的线程池大小的函数,还是请求吞吐量或两者兼而有之?

-> 正如我上面提到的,这取决于您正在执行的任务类型。您应该使用公共线程池来执行这些任务。

有没有更好的方法来优化这种情况,以便服务线程在大多数时候不会在执行 IO 时被阻塞?

-> 当一个线程进行 IO 操作并且不需要 CPU 时,您应该对线程池大小和操作系统自动分配 CPU 给另一个线程进行基准测试。


推荐阅读