我刚刚将 Spring Boot 应用程序升级到 Java 21。作为其中的一部分,我还进行了更改以使用虚拟线程。无论是在服务 API 请求时还是在使用执行器在内部执行异步操作时。
对于一个用例,由虚拟线程驱动的执行器的性能似乎比由操作系统线程驱动的
ForkJoinPool
更差。此用例是设置一些 MDC 值并通过 HTTP 调用外部系统。
这是我的伪代码:
List<...> ... = executorService.submit(
() -> IntStream.rangeClosed(-from, to)
.mapToObj(i -> ...)
.parallel()
.map(... -> {
try {
service.setSomeThreadLocalString(...);
MDC.put(..., ...);
MDC.put(..., ...);
return service.call(...);
} finally {
service.removeSomeThreadLocalString(...);
MDC.remove(...);
MDC.remove(...);
}
})
.toList())
.get();
ExecutorService 是:
new ForkJoinPool(30)
Executors.newVirtualThreadPerTaskExecutor()
如果有任何区别,请在 Spring Boot 中启用此属性:
spring:
threads:
virtual:
enabled: true
有什么想法为什么会发生这种情况吗?
默认情况下,Java 会分配一个操作系统线程池(一个工作窃取 fork-join 池),其并行度等于虚拟线程执行的处理器数量(另请参阅
JEP 444:虚拟线程)。如果您在 Kubernetes 上运行,并且 pod 有 1 个 CPU(或少于 1 个 CPU),Java 将假定处理器数量为 1。
您可以通过传递-XX:ActiveProcessorCount=n
设置来告诉 Java 假定不同数量的处理器,其中
n
是 Java 应假定的处理器数量。或者,或者另外,您可以设置系统属性
jdk.virtualThreadScheduler.parallelism
(使用
-Djdk.virtualThreadScheduler.parallelism=n
,其中
n
是所需的并行度)。这两个都需要传递给命令行上的
java
可执行文件。