Java 21 虚拟线程执行器的性能比具有池化操作系统线程的执行器差?

问题描述 投票:0回答:1

我刚刚将 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 是:

  1. new ForkJoinPool(30)
    
    
  2. Executors.newVirtualThreadPerTaskExecutor()
    
    
看起来选项 1 的性能比选项 2 好很多。有时它比选项 1 快 100%。我在 Java 21 环境中完成了这个测试。我正在测试 10 个并行执行。选项 1 通常需要 800-1000 毫秒,选项 2 需要 1500-2000 毫秒。

如果有任何区别,请在 Spring Boot 中启用此属性:

spring: threads: virtual: enabled: true
有什么想法为什么会发生这种情况吗?

java executorservice java-21 virtual-threads
1个回答
0
投票
正如您在评论中指出的,您使用的是配置有 1 个 CPU 的 Kubernetes 容器,问题在于您实际上是将并行度为 30 的 fork-join 池与并行度为 1 的虚拟线程执行进行比较。

默认情况下,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
 可执行文件。

© www.soinside.com 2019 - 2024. All rights reserved.