【Java】虚拟线程与Java 8普通线程池的对比

ops/2024/10/17 21:44:09/

文章目录

      • IO密集型任务
      • 高并发Web服务器
      • 异步编程
      • 微服务架构
      • 大规模并行处理
      • 事件驱动的应用
      • 不适用的场景
      • 使用对比
      • 性能分析
        • 资源消耗
        • 并发能力
        • 性能测试
      • 总结

在JDK 21之前,Java并发编程主要依赖于传统的线程池,如Java 8中的 Executors.newFixedThreadPool()

虚拟线程(Virtual Threads)是JDK 21引入的一种新型线程,它们旨在解决传统线程在轻量级并发任务处理中的局限性。以下是虚拟线程适用的场景:

IO密集型任务

虚拟线程特别适用于IO密集型任务,因为这些任务通常会花费大量时间等待外部资源,如网络响应、文件读写等。在等待期间,虚拟线程可以被挂起,而不占用操作系统线程资源,从而允许更多的虚拟线程在同一时间内运行。

高并发Web服务器

在Web服务器中,通常需要处理大量的并发请求,这些请求往往是IO密集型的。使用虚拟线程可以显著提高服务器的并发处理能力,因为它们能够以极低的成本创建数百万个线程。

异步编程

虚拟线程与Project Loom中的结构化并发(Structured Concurrency)相结合,为异步编程提供了更好的支持。这使得编写和维护异步代码更加容易。

微服务架构

在微服务架构中,服务之间通常会有大量的网络调用。虚拟线程可以用来处理这些网络调用,提高服务的响应速度和吞吐量。

大规模并行处理

当需要并行处理大量任务时,如大数据处理、分布式计算等,虚拟线程可以提供更高的并行度,因为它们不受物理线程数量的限制。

事件驱动的应用

事件驱动的应用,如消息队列消费者、事件流处理等,可以利用虚拟线程来处理大量的事件,而不需要为每个事件分配一个操作系统线程。

不适用的场景

虽然虚拟线程在许多场景下都非常有用,但以下场景可能不太适合使用虚拟线程

  • CPU密集型任务虚拟线程并不提供比传统线程更好的CPU利用率。对于CPU密集型任务,操作系统线程可能更合适,因为它们可以直接映射到CPU核心。
  • 需要精确线程控制的场景:如果应用程序需要精细控制线程的调度和行为,使用传统线程可能更为合适。
    总之,虚拟线程适用于那些需要大量并发、高吞吐量、且任务执行时间主要花费在等待外部资源响应的场景。在实际应用中,开发者应根据具体的应用需求和资源特性来决定是否使用虚拟线程

使用对比

Java 8普通线程池

在Java 8中,创建一个固定大小的线程池并执行任务通常如下:

java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.stream.IntStream;
public class ThreadPoolExample {public static void main(String[] args) {// 创建一个固定大小的线程池ExecutorService executor = Executors.newFixedThreadPool(10);// 使用IntStream并发处理10000个任务IntStream.range(0, 10000).forEach(i -> {executor.submit(() -> {System.out.println("处理任务:" + i);// 模拟任务执行时间try {Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}});});// 关闭线程池executor.shutdown();}
}

在这个例子中,我们创建了一个固定大小为10的线程池。这意味着即使我们有10000个任务,同一时间也只有10个任务在执行。

虚拟线程

而使用虚拟线程的示例已经在之前的段落中给出。虚拟线程可以创建数百万个,而不受物理线程数量的限制。

性能分析

资源消耗
  • 普通线程池:每个线程都对应一个操作系统线程,创建和销毁线程的成本较高,且占用较多的内存和处理器资源。
  • 虚拟线程虚拟线程是轻量级的,它们共享同一个或几个操作系统线程,因此创建和销毁的成本非常低,且占用的资源远少于普通线程。
并发能力
  • 普通线程池:由于操作系统线程资源的限制,线程池的大小通常受限,这限制了应用程序的并发能力。
  • 虚拟线程:由于虚拟线程的资源消耗非常低,可以创建大量的虚拟线程,从而实现更高的并发能力。
性能测试

以下是一个简单的性能测试,比较在处理大量任务时,普通线程池和虚拟线程的性能差异。

java">import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.stream.IntStream;
public class PerformanceTest {public static void main(String[] args) throws InterruptedException {// 普通线程池性能测试long startTimePool = System.nanoTime();ExecutorService poolExecutor = Executors.newFixedThreadPool(100);IntStream.range(0, 1000000).forEach(i -> poolExecutor.submit(() -> {// 模拟任务执行}));poolExecutor.shutdown();poolExecutor.awaitTermination(1, TimeUnit.HOURS);long endTimePool = System.nanoTime();// 虚拟线程性能测试long startTimeVirtual = System.nanoTime();ExecutorService virtualExecutor = Executors.newVirtualThreadPerTaskExecutor();IntStream.range(0, 1000000).forEach(i -> virtualExecutor.submit(() -> {// 模拟任务执行}));virtualExecutor.shutdown();virtualExecutor.awaitTermination(1, TimeUnit.HOURS);long endTimeVirtual = System.nanoTime();// 输出结果System.out.println("普通线程池执行时间: " + TimeUnit.NANOSECONDS.toMillis(endTimePool - startTimePool) + " ms");System.out.println("虚拟线程执行时间: " + TimeUnit.NANOSECONDS.toMillis(endTimeVirtual - startTimeVirtual) + " ms");}
}

在这个测试中,我们分别用普通线程池和虚拟线程执行了100万个任务。虚拟线程通常会比普通线程池更快完成这些任务,因为它们创建和切换的成本更低。

总结

虚拟线程在处理大量并发任务时,相比Java 8普通线程池具有明显的性能优势。它们更加轻量级,可以创建更多数量的线程,从而提高应用程序的并发处理能力。然而,虚拟线程也并非万能,它们适用于IO密集型任务,而在CPU密集型任务中,传统线程可能仍然具有优势。在实际开发中,应根据具体场景选择合适的并发模型。


http://www.ppmy.cn/ops/119194.html

相关文章

【C语言】指针详解(一)

个人主页 : zxctscl 如有转载请先通知 文章目录 1.内存与地址2.指针变量与地址2.1 取地址操作符&2.2 指针变量2.3 指针类型2.4 解引用操作符2.5 指针变量的大小 3. 指针变量类型的意义3.1 指针的解引用 4. const修饰指针4.1 const修饰变量4.2 const修饰指针变量…

RK3568 android11 适配鼎桥MT5710-CN 5G模块

一,概述 鼎桥MT571X设备和Android系统主要通过USB接口进行数据通信,Android系统上的Linux内核需要根据鼎桥模块设备上报的USB设备接口加载USB驱动,USB驱动正确加载后,鼎桥模块才能正常工作。 Android系统中支持鼎桥模块设备相关的Linux内核驱动架构,如下图所示: 在Lin…

d2l | 目标检测数据集:RuntimeError: No such operator image::read_file

目录 1 存在的问题2 可能的解决方案3 最终的解决方案3.1 方案一(我已弃用)3.2 方案二(基于方案一)3.3 方案三(基于方案一) 1 存在的问题 李沐老师提供的读取香蕉数据集的函数如下: def…

PHP中对数组序列化和反序列化的函数

在PHP中,对数组进行序列化和反序列化的函数分别是 serialize() 和 unserialize()。 序列化(Serialize) serialize() 函数用于将PHP的值或对象转换为一个可存储或传输的字符串表示。这通常用于将数组、对象等复杂数据类型转换为字符串&#…

YOLOv11改进策略【损失函数篇】| Slide Loss,解决简单样本和困难样本之间的不平衡问题

一、本文介绍 本文记录的是改进YOLOv11的损失函数,将其替换成Slide Loss,并详细说明了优化原因,注意事项等。Slide Loss函数可以有效地解决样本不平衡问题,为困难样本赋予更高的权重,使模型在训练过程中更加关注困难样…

强化学习-python案例

强化学习是一种机器学习方法,旨在通过与环境的交互来学习最优策略。它的核心概念是智能体(agent)在环境中采取动作,从而获得奖励或惩罚。智能体的目标是最大化长期奖励,通过试错的方式不断改进其决策策略。 在强化学习…

C++之 友元重载 以及最常用的几种友元函数

在之前的友元中就曾经讲过,我们为了去访问修改私有成员中的数据时,只能通过公有的办法去进行访问操作,非常的局限。所以C引用了友元函数,只要加上friend关键字,C的这个类,会自动把这个函数的权限拉到类内&a…

【Linux 从基础到进阶】HBase数据库安装与配置

HBase数据库安装与配置 Apache HBase 是一个开源的、分布式的、面向列的数据库,基于 Hadoop 的 HDFS 构建,适用于需要随机读写大量数据的场景。HBase 提供了强大的容错和线性扩展能力,支持高并发的读写操作,广泛应用于大数据分析和实时应用系统中。 本文将介绍 HBase 的安…