当前位置: 首页 > article >正文

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

文章目录

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

在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中,创建一个固定大小的线程池并执行任务通常如下:

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个任务在执行。

虚拟线程

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

性能分析

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

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

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.kler.cn/news/327834.html

相关文章:

  • PHP反序列化1(基础入门)
  • 单片机内存保护-MPU(Memory Protection Unit)
  • 【最新华为OD机试E卷-支持在线评测】字符串变换最小字符串(100分)多语言题解-(Python/C/JavaScript/Java/Cpp)
  • CSS | 面试题:你知道几种移动端适配方案?
  • Git常用方法——详解
  • 【解密 Kotlin 扩展函数】扩展属性与扩展函数类似(十九)
  • 网络协议 TCP、UDP 和 HTTP
  • 解决QWidget及内部控件只能放大不能缩小的问题
  • Vscode: reason: oom, code: -536870904
  • VBA数据库解决方案第十五讲:Recordset集合中单个数据的精确处理
  • 【无标题】observer: error while loading shared libraries: libmariadb.so.3处理办法
  • 以串口接口为例介绍关于BSP底层架构开发的迭代过程
  • LeetCode 983.最低票价
  • 前端面试:项目细节重难点问题分享(17)
  • 无环SLAM系统集成后端回环检测模块(loop):SC-A-LOAM以及FAST_LIO_SLAM
  • 基于MTK7981平台,学习了解理解SoC上电和boot流程
  • Thinkphp/Laravel基于vue的的出版社书籍阅读管理系统
  • Docker笔记-Docker磁盘空间清理
  • c#中的功能优势
  • 【QT Quick】基础语法:文件定义类型与枚举类型
  • 最大正方形 Python题解
  • windows下安装nginx和基本配置
  • cfg80211是怎么配置无线设备的AP的?
  • Python与MongoDB交互
  • 《一本书讲透Elasticsearch》读书笔记-索引
  • 2024年主流前端框架的比较和选择指南
  • 【学术会议征稿】2024年遥感技术与图像处理国际学术会议(RSTIP 2024)
  • taro RN 左右滑动切换页面
  • 自动驾驶 3DGS 学习笔记
  • 接口性能优化日记