java - 在服务中配置线程池大小
问题描述
我正在编写一个服务,它需要两个 urlurlA
并urlB
获取两个整数a
和b
. 该服务返回sum
和。a
b
以最简单的形式,该服务的工作方式如下:
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 大小的线程池。
我的问题是:
- 上述理解(斜体部分)是否正确?
- 如果是,我应该如何选择线程池的最佳大小。它应该是我的服务容器的线程池大小的函数,还是请求吞吐量或两者兼而有之?
- 有没有更好的方法来优化这种情况,以便服务线程在大多数情况下不会在执行 IO 时被阻塞。
注意:此问题中提供的详细信息并非完全真实的场景,而是代表回答问题所需的同一组复杂性。
解决方案
如果您的函数getSumFromUrls
在每次新请求到来时都执行,这意味着它将每次创建一个新的线程池并提交任务。假设如果您1000
在任何时间点都有请求命中,那么1000
将创建 ThreadPool 并最终创建1000s
线程。我相信如果您在任何时候创建 1000 或更多线程,这对您的应用程序来说将是一个问题。通常在任何时间点,活动线程的数量应该大约/等于available core
系统大小的数量,但是这完全取决于用例假设你的任务是CPU intensive
线程数应该是 CPU 核心大小但是如果你任务是IO intensive
那么你可以有更多的线程数。更多数量的线程意味着将发生更多数量的上下文切换,这有其自身的成本并可能降低应用程序性能。
上述理解(斜体部分)是否正确?
-> 是的。
如果是,我应该如何选择线程池的最佳大小。它应该是我的服务容器的线程池大小的函数,还是请求吞吐量或两者兼而有之?
-> 正如我上面提到的,这取决于您正在执行的任务类型。您应该使用公共线程池来执行这些任务。
有没有更好的方法来优化这种情况,以便服务线程在大多数时候不会在执行 IO 时被阻塞?
-> 当一个线程进行 IO 操作并且不需要 CPU 时,您应该对线程池大小和操作系统自动分配 CPU 给另一个线程进行基准测试。
推荐阅读
- github - 即使我有 1 次提交,我的用户名也会在 github 上出现两次?
- c++ - QT项目和USB连接
- python-3.x - 访问数据框列中的子字符串以创建新列
- c - 仅将文本的一部分放入数组
- r - 错误调试:使用 cbind 将列移动到另一个数据框中
- javascript - Firebase 云消息传递的正则表达式
- javascript - 删除外部对象中的对象不起作用
- laravel - 使用 Laravel 8 从 Redis 中删除延迟的作业
- spring-boot - spring-data-jpa hibernate:无法延迟初始化集合,无法初始化代理 - 没有会话
- android - MAPBOX:当我运行没有错误的应用程序时,地图没有出现(在 Kotlin 中开发 - 用于 android - 片段中的地图)