type
status
date
slug
summary
tags
category
icon
password
线程池和虚拟线程
Java 的 虚拟线程(Virtual Threads) 是在 JDK 19 中作为预览功能引入,并在 JDK 21 中正式推出的功能(Project Loom 的一部分)。它与传统的 线程池(Thread Pool) 相比,在 效率和便利性 方面各有优劣:

1. 效率对比

指标
虚拟线程
线程池(固定大小)
线程数量
可创建百万级线程
一般几十到上千个(受限于内存和 CPU)
资源消耗
轻量级,消耗少量内存
每个线程占用较大栈空间(1MB 左右)
调度机制
用户态调度(由 JVM 管理,非 OS 线程)
内核态调度(受操作系统管理)
上下文切换
零成本阻塞,IO 等待不会影响其他线程
上下文切换成本高,阻塞会影响整体吞吐
适用场景
高并发、IO 密集型(如 Web 请求、爬虫)
CPU 密集型(计算任务、图像处理)
  • 虚拟线程的核心优势它们不与 OS 线程一一对应,而是由 JVM 管理的 "纤程"(类似 Go 协程)。当一个虚拟线程执行 IO 操作(如数据库查询、网络请求),JVM 会自动挂起该线程,释放底层 OS 线程,从而避免不必要的 CPU 消耗。当 IO 完成时,JVM 会把刚才挂起的虚拟线程“恢复”,它会安排虚拟线程继续运行(可能在原平台线程上,也可能在别的平台线程上)。
  • 线程池的限制传统线程池的大小受限于CPU 核心数,过多线程会导致线程上下文切换(context switch),降低性能。因此,线程池更适合 CPU 密集型任务

2. 便利性对比

指标
虚拟线程
线程池(ExecutorService)
创建方式
Thread.startVirtualThread(() -> {...})
Executors.newFixedThreadPool(n)
编程风格
直接启动线程,类似传统 Thread
需管理池子,提交任务
阻塞友好性
Thread.sleep()socket.read() 不会影响吞吐
线程池需要优化(如 CompletableFuture
代码简洁性
更简单,不需要管理线程池
需要调优参数(池大小、队列策略)
任务调度
JVM 自动管理(无需手动调度)
需要管理任务队列和拒绝策略
  • 虚拟线程的便利性由于虚拟线程的创建成本极低,我们可以像直接使用 Thread 一样编写并发代码,而不必手动管理线程池。例如:
    • 而传统线程池的用法较为繁琐:
  • 线程池的额外负担需要手动调优线程数任务队列拒绝策略,在高并发情况下管理复杂。

3. 适用场景

场景
推荐方案
理由
高并发 Web 服务
虚拟线程
轻量级、零成本阻塞,适合 IO 密集型任务
数据库访问
虚拟线程
避免数据库 IO 操作时占用 OS 线程
CPU 密集型计算
线程池
避免创建大量线程,减少调度开销
长时间运行任务
线程池
线程池可管理长生命周期任务
  • 如果你的任务主要是IO 密集型(比如 Web 服务器、爬虫、数据库访问),虚拟线程更适合
  • 如果你的任务是CPU 密集型(比如科学计算、加密解密),线程池仍然是更优解,因为虚拟线程不能提升 CPU 计算效率。

4. 线程池 vs. 虚拟线程:什么时候用哪个?

使用场景
选项
高并发(>10 万)
虚拟线程
任务短、快且多
虚拟线程
高 IO 操作
虚拟线程
CPU 密集
线程池
固定并发数
线程池
如果你不确定:
  1. IO 密集型任务使用虚拟线程
  1. CPU 密集型任务使用固定大小的线程池(线程数 = CPU 核心数)。

5. 总结

  • 虚拟线程的优势
    • 极低开销(百万级并发)
    • 零成本阻塞(适用于 IO 密集型任务)
    • 编程方式更简单
  • 虚拟线程的局限
    • 不适合 CPU 密集任务(OS 线程池仍然更高效)
    • JVM 调度仍在优化(某些场景下可能不如手动管理线程池)
  • 线程池的优势
    • 适合 CPU 密集型任务
    • 可以限制线程数,减少竞争
    • 成熟、稳定、可控

什么时候使用虚拟线程?

当你需要高并发处理 IO 任务时,虚拟线程是最好的选择(如 Web 服务、爬虫、数据库访问)。如果你的代码原本是同步的,但你希望提高吞吐量,虚拟线程可以无痛替换普通线程

什么时候继续使用线程池?

CPU 密集型任务(如大规模计算、多线程压缩等),线程池的效率更高。需要长期运行的任务(如后台任务),线程池管理起来更可控。

一句话总结

虚拟线程适用于IO 密集型高并发任务,而线程池仍然是CPU 密集任务的最佳选择。
Git异步和并发
Loading...