Java 21 虚拟线程 vs 缓存线程池与固定线程池

2025-12-12 0 481

引言

并发编程仍然是构建可扩展、响应式 Java 应用程序的关键部分。多年来,Java 持续增强了其多线程编程能力。本文回顾了从 Java 8 到 Java 21 并发的演进,重点介绍了重要的改进以及 Java 21 中引入的具有重大影响的虚拟线程。

从 Java 8 开始,并发 API 出现了显著的增强,例如原子变量、并发映射以及集成 lambda 表达式以实现更具表现力的并行编程。

Java 8 引入的关键改进包括:

  • 线程与执行器

  • 同步与锁

  • 原子变量与 ConcurrentMap

Java 21 于 2023 年底发布,带来了虚拟线程这一重大演进,从根本上改变了 Java 应用程序处理大量并发任务的方式。虚拟线程为服务器应用程序提供了更高的可扩展性,同时保持了熟悉的\”每个请求一个线程\”的编程模型。

或许,Java 21 中最重要的特性就是虚拟线程。

在 Java 21 中,Java 的基本并发模型保持不变,Stream API 仍然是并行处理大型数据集的首选方式。

随着虚拟线程的引入,并发 API 现在能提供更好的性能。在当今的微服务和可扩展服务器应用领域,线程数量必须增长以满足需求。虚拟线程的主要目标是使服务器应用程序能够实现高可扩展性,同时仍使用简单的\”每个请求一个线程\”模型。

虚拟线程

在 Java 21 之前,JDK 的线程实现使用的是操作系统线程的薄包装器。然而,操作系统线程代价高昂:

  • 如果每个请求在其整个持续时间内消耗一个操作系统线程,线程数量很快就会成为可扩展性的瓶颈。

  • 即使使用线程池,吞吐量仍然受到限制,因为实际线程数量是有上限的。

虚拟线程的目标是打破 Java 线程与操作系统线程之间的 1:1 关系。

虚拟线程应用了类似于虚拟内存的概念。正如虚拟内存将大的地址空间映射到较小的物理内存一样,虚拟线程允许运行时通过将它们映射到少量操作系统线程来制造拥有许多线程的假象。

平台线程是操作系统线程的薄包装器。

而虚拟线程并不绑定到任何特定的操作系统线程。虚拟线程可以执行平台线程可以运行的任何代码。这是一个主要优势——现有的 Java 代码通常无需修改或仅需少量修改即可在虚拟线程上运行。虚拟线程由平台线程承载,这些平台线程仍然由操作系统调度。

例如,您可以像这样创建一个使用虚拟线程的执行器:


ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();

对比示例

虚拟线程仅在主动执行 CPU 密集型任务时才消耗操作系统线程。虚拟线程在其生命周期内可以在不同的载体线程上挂载或卸载。

通常,当虚拟线程遇到阻塞操作时,它会自行卸载。一旦该阻塞任务完成,虚拟线程通过挂载到任何可用的载体线程上来恢复执行。这种挂载和卸载过程频繁且透明地发生——不会阻塞操作系统线程。

示例 — 源代码

Example01CachedThreadPool.java

在此示例中,使用缓存线程池创建了一个执行器:


var executor = Executors.newCachedThreadPool()


package threads;

  


import java.time.Duration;

import java.util.concurrent.Executors;

import java.util.stream.IntStream;

  


/**

*

* @author Milan Karajovic 

*

*/

  


public class Example01CachedThreadPool {

  


public void executeTasks(final int NUMBER_OF_TASKS) {

  


final int BLOCKING_CALL = 1;

System.out.println(\"Number of tasks which executed using \'newCachedThreadPool()\' \" + NUMBER_OF_TASKS + \" tasks each.\");

  


long startTime = System.currentTimeMillis();

  


try (var executor = Executors.newCachedThreadPool()) {

  


IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {

executor.submit(() -> {

// 模拟阻塞调用

Thread.sleep(Duration.ofSeconds(BLOCKING_CALL));

return i;

});

});

  


} catch (Exception e) {

throw new RuntimeException(e);

}

  


long endTime = System.currentTimeMillis();

System.out.println(\"For executing \" + NUMBER_OF_TASKS + \" tasks duration is: \" + (endTime - startTime) + \" ms\");

}

  


}


package threads;

  


import org.junit.jupiter.api.MethodOrderer;

import org.junit.jupiter.api.Order;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.TestMethodOrder;

  


/**

*

* @author Milan Karajovic 

*

*/

  


@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class Example01CachedThreadPoolTest {

  


@Test

@Order(1)

public void test_1000_tasks() {

Example01CachedThreadPool example01CachedThreadPool = new Example01CachedThreadPool();

example01CachedThreadPool.executeTasks(1000);

}

  


@Test

@Order(2)

public void test_10_000_tasks() {

Example01CachedThreadPool example01CachedThreadPool = new Example01CachedThreadPool();

example01CachedThreadPool.executeTasks(10_000);

}

  


@Test

@Order(3)

public void test_100_000_tasks() {

Example01CachedThreadPool example01CachedThreadPool = new Example01CachedThreadPool();

example01CachedThreadPool.executeTasks(100_000);

}

  


@Test

@Order(4)

public void test_1_000_000_tasks() {

Example01CachedThreadPool example01CachedThreadPool = new Example01CachedThreadPool();

example01CachedThreadPool.executeTasks(1_000_000);

}

  


}

我 PC 上的测试结果:

Example02FixedThreadPool.java

使用固定线程池创建执行器:


var executor = Executors.newFixedThreadPool(500)


package threads;

  


import java.time.Duration;

import java.util.concurrent.Executors;

import java.util.stream.IntStream;

  


/**

*

* @author Milan Karajovic 

*

*/

  


public class Example02FixedThreadPool {

  


public void executeTasks(final int NUMBER_OF_TASKS) {

  


final int BLOCKING_CALL = 1;

System.out.println(\"Number of tasks which executed using \'newFixedThreadPool(500)\' \" + NUMBER_OF_TASKS + \" tasks each.\");

  


long startTime = System.currentTimeMillis();

  


try (var executor = Executors.newFixedThreadPool(500)) {

  


IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {

executor.submit(() -> {

// 模拟阻塞调用

Thread.sleep(Duration.ofSeconds(BLOCKING_CALL));

return i;

});

});

  


} catch (Exception e) {

throw new RuntimeException(e);

}

  


long endTime = System.currentTimeMillis();

System.out.println(\"For executing \" + NUMBER_OF_TASKS + \" tasks duration is: \" + (endTime - startTime) + \" ms\");

}

  


}


package threads;

  


import org.junit.jupiter.api.MethodOrderer;

import org.junit.jupiter.api.Order;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.TestMethodOrder;

  


/**

*

* @author Milan Karajovic 

*

*/

  


@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class Example02FixedThreadPoolTest {

  


@Test

@Order(1)

public void test_1000_tasks() {

Example02FixedThreadPool example02FixedThreadPool = new Example02FixedThreadPool();

example02FixedThreadPool.executeTasks(1000);

}

  


@Test

@Order(2)

public void test_10_000_tasks() {

Example02FixedThreadPool example02FixedThreadPool = new Example02FixedThreadPool();

example02FixedThreadPool.executeTasks(10_000);

}

  


@Test

@Order(3)

public void test_100_000_tasks() {

Example02FixedThreadPool example02FixedThreadPool = new Example02FixedThreadPool();

example02FixedThreadPool.executeTasks(100_000);

}

  


@Test

@Order(4)

public void test_1_000_000_tasks() {

Example02FixedThreadPool example02FixedThreadPool = new Example02FixedThreadPool();

example02FixedThreadPool.executeTasks(1_000_000);

}

  


}

我 PC 上的测试结果:

Example03VirtualThread.java

使用虚拟线程每任务执行器创建执行器:


var executor = Executors.newVirtualThreadPerTaskExecutor()


package threads;

  


import java.time.Duration;

import java.util.concurrent.ExecutorService;

import java.util.concurrent.Executors;

import java.util.stream.IntStream;

  


/**

*

* @author Milan Karajovic 

*

*/

  


public class Example03VirtualThread {

  


public void executeTasks(final int NUMBER_OF_TASKS) {

  


final int BLOCKING_CALL = 1;

System.out.println(\"Number of tasks which executed using \'newVirtualThreadPerTaskExecutor()\' \" + NUMBER_OF_TASKS + \" tasks each.\");

  


long startTime = System.currentTimeMillis();

  


try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {

  


IntStream.range(0, NUMBER_OF_TASKS).forEach(i -> {

executor.submit(() -> {

// 模拟阻塞调用

Thread.sleep(Duration.ofSeconds(BLOCKING_CALL));

return i;

});

});

  


} catch (Exception e) {

throw new RuntimeException(e);

}

  


long endTime = System.currentTimeMillis();

System.out.println(\"For executing \" + NUMBER_OF_TASKS + \" tasks duration is: \" + (endTime - startTime) + \" ms\");

}

  


}


package threads;

  


import org.junit.jupiter.api.MethodOrderer;

import org.junit.jupiter.api.Order;

import org.junit.jupiter.api.Test;

import org.junit.jupiter.api.TestMethodOrder;

  


/**

*

* @author Milan Karajovic 

*

*/

  


@TestMethodOrder(MethodOrderer.OrderAnnotation.class)

public class Example03VirtualThreadTest {

  


@Test

@Order(1)

public void test_1000_tasks() {

Example03VirtualThread example03VirtualThread = new Example03VirtualThread();

example03VirtualThread.executeTasks(1000);

}

  


@Test

@Order(2)

public void test_10_000_tasks() {

Example03VirtualThread example03VirtualThread = new Example03VirtualThread();

example03VirtualThread.executeTasks(10_000);

}

  


@Test

@Order(3)

public void test_100_000_tasks() {

Example03VirtualThread example03VirtualThread = new Example03VirtualThread();

example03VirtualThread.executeTasks(100_000);

}

  


@Test

@Order(4)

public void test_1_000_000_tasks() {

Example03VirtualThread example03VirtualThread = new Example03VirtualThread();

example03VirtualThread.executeTasks(1_000_000);

}

  


@Test

@Order(5)

public void test_2_000_000_tasks() {

Example03VirtualThread example03VirtualThread = new Example03VirtualThread();

example03VirtualThread.executeTasks(2_000_000);

}

  


}

我 PC 上的测试结果:

结论

您可以清楚地看到用于处理所有 NUMBER_OF_TASKS 的不同执行器实现之间的执行时间差异。值得尝试不同的 NUMBER_OF_TASKS 值以观察性能变化。

虚拟线程的优势在处理大量任务时变得尤其明显。当 NUMBER_OF_TASKS 设置为较高的数值时——例如 1,000,000——性能差距是显著的。如下表所示,虚拟线程在处理大量任务时效率要高得多:

我确信,在澄清这一点之后,如果您的应用程序使用并发 API 处理大量任务,您会认真考虑迁移到 Java 21 并利用虚拟线程。在许多情况下,这种转变可以显著提高应用程序的性能和可扩展性。

源代码:GitHub Repository – Comparing Threads in Java 21


【注】本文译自:Java 21 Virtual Threads vs Cached and Fixed Threads

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

申明:本文由第三方发布,内容仅代表作者观点,与本网站无关。对本文以及其中全部或者部分内容的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。本网发布或转载文章出于传递更多信息之目的,并不意味着赞同其观点或证实其描述,也不代表本网对其真实性负责。

左子网 编程相关 Java 21 虚拟线程 vs 缓存线程池与固定线程池 https://www.zuozi.net/35789.html

常见问题
  • 1、自动:拍下后,点击(下载)链接即可下载;2、手动:拍下后,联系卖家发放即可或者联系官方找开发者发货。
查看详情
  • 1、源码默认交易周期:手动发货商品为1-3天,并且用户付款金额将会进入平台担保直到交易完成或者3-7天即可发放,如遇纠纷无限期延长收款金额直至纠纷解决或者退款!;
查看详情
  • 1、描述:源码描述(含标题)与实际源码不一致的(例:货不对板); 2、演示:有演示站时,与实际源码小于95%一致的(但描述中有”不保证完全一样、有变化的可能性”类似显著声明的除外); 3、发货:不发货可无理由退款; 4、安装:免费提供安装服务的源码但卖家不履行的; 5、收费:价格虚标,额外收取其他费用的(但描述中有显著声明或双方交易前有商定的除外); 6、其他:如质量方面的硬性常规问题BUG等。 注:经核实符合上述任一,均支持退款,但卖家予以积极解决问题则除外。
查看详情
  • 1、左子会对双方交易的过程及交易商品的快照进行永久存档,以确保交易的真实、有效、安全! 2、左子无法对如“永久包更新”、“永久技术支持”等类似交易之后的商家承诺做担保,请买家自行鉴别; 3、在源码同时有网站演示与图片演示,且站演与图演不一致时,默认按图演作为纠纷评判依据(特别声明或有商定除外); 4、在没有”无任何正当退款依据”的前提下,商品写有”一旦售出,概不支持退款”等类似的声明,视为无效声明; 5、在未拍下前,双方在QQ上所商定的交易内容,亦可成为纠纷评判依据(商定与描述冲突时,商定为准); 6、因聊天记录可作为纠纷评判依据,故双方联系时,只与对方在左子上所留的QQ、手机号沟通,以防对方不承认自我承诺。 7、虽然交易产生纠纷的几率很小,但一定要保留如聊天记录、手机短信等这样的重要信息,以防产生纠纷时便于左子介入快速处理。
查看详情

相关文章

猜你喜欢
发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务