Kotlin 协程 vs Java 虚拟线程:两种并发模型的对比

在现代开发中,我们越来越多地写出这样的代码:

看起来是同步调用,但它们却是并发执行的。

再看 Java:

也是同步写法,但在虚拟线程下,同样可以并发运行。

这就引出了一个很有意思的问题:

👉 为什么代码是"同步的",执行却是"异步的"?

围绕这个现象,JVM 生态走出了两条不同的路径:

  • Java 虚拟线程(Virtual Threads)
  • Kotlin 协程(Coroutines)

它们都在做同一件事:

让你用同步的方式,写出异步的代码

但实现方式,却完全不同。

🧪 实验场景

统一场景:

并发请求 WanAndroid 接口,然后汇总输出结果

1. 先看我的两份对照代码在做什么

Java 版:虚拟线程 + 阻塞式 Retrofit

Java 示例的核心逻辑是:

真正执行请求时,用的依然是 Retrofit 的阻塞式调用:

也就是说,这套写法本质上还是同步阻塞风格,只不过任务跑在虚拟线程里。


Kotlin 版:协程 + suspend Retrofit

Kotlin 示例的核心逻辑是:

对应的 Retrofit 接口定义是:

这里的特点很明显:

  • API 本身就是 suspend
  • 并发通过 async
  • 汇总通过 await
  • 外层由 coroutineScope 统一管理

2. Java 虚拟线程是什么

Java 21 正式发布了虚拟线程,这是 Project Loom 最重要的成果之一。

传统平台线程直接映射到操作系统线程,创建和切换成本都比较高。

如果我们有大量 I/O 阻塞任务,比如网络请求、数据库访问、文件读写,那么很容易出现:

  • 线程数越来越多
  • 很多线程都在"等"
  • 系统资源被大量线程占住

虚拟线程的思路是:

  • 开发者继续按线程模型写代码
  • JVM 在底层负责调度这些轻量线程
  • 当虚拟线程发生阻塞时,JVM 可以把它从 carrier thread 上卸下来
  • 让底层平台线程继续执行其他任务

所以虚拟线程的关键价值是:

保留阻塞式编程模型,但把线程成本降下来。

这点和我 Java 示例里的写法非常一致:

  • 还是 Callable
  • 还是 Future
  • 还是 call.execute()
  • 只是执行器换成了 newVirtualThreadPerTaskExecutor()

3. Kotlin 协程是什么

Kotlin 协程是语言和库层面的并发抽象。

它不是线程,也不是简单的线程池封装。

协程更关心的是:

  • 哪些地方可以挂起
  • 哪些任务要并发
  • 任务之间的父子关系是什么
  • 异常和取消怎么传播
  • 生命周期怎么管理

在我的 Kotlin 示例里,api.banner()api.homeArticles() 都是 suspend fun

这意味着它们在语义上就是"可挂起"的操作,而不是普通阻塞函数。

所以协程的核心价值不是"线程更便宜",而是:

让异步流程在代码层就具备结构化表达能力。


4. 它们的相同点:都能把并发 API 调用写得更自然

虽然虚拟线程和协程本质不同,但在并发 API 调用这个场景里,它们的目标是一致的。

4.1 都适合 I/O 密集型任务

比如:

  • HTTP 请求
  • 数据库查询
  • 文件读取
  • 消息队列消费

这类任务共同特点是:等待时间远大于计算时间

无论虚拟线程还是协程,都非常适合这种场景。


4.2 都能让代码接近同步思维

Java 版中,业务逻辑看起来依然是同步代码:

Kotlin 版中,调用看起来也很自然:

这其实是两种技术都非常吸引人的地方:

  • 不需要写复杂回调
  • 不需要把业务逻辑切碎
  • 并发代码可读性明显更好

4.3 都能显著提升并发吞吐能力

无论是虚拟线程还是协程,它们本质上都在提升 I/O 等待期间的资源利用率。

所以如果你的服务瓶颈主要在 I/O,而不是 CPU 计算,两者都可能带来明显收益。


5. 它们最大的不同点:解决问题的层次不同

Java 虚拟线程和 Kotlin 协程都想解决同一类问题:让你用"看起来同步"的代码写高并发 I/O,避免传统平台线程太贵。但它们的抽象层级和调度模型不同。

Java 虚拟线程解决的是"线程太贵",Kotlin 协程解决的是"异步任务怎么组织和治理"。

5.1 虚拟线程更像"线程模型升级"

从我的 Java 代码就能看出来:

  • 任务仍然是线程任务
  • 结果仍然是 Future
  • 请求仍然是阻塞式调用
  • 并发依然通过 Executor 调度

所以虚拟线程并没有改变你的业务编程范式。

它做的是:在底层把线程变轻。

这对 Java 服务端尤其友好,因为可以低成本承接大量已有阻塞代码。


5.2 协程更像"并发治理模型升级"

而 Kotlin 协程从写法上就已经体现出另一种思路:

  • suspend
  • coroutineScope
  • async
  • await

这些 API 不只是"把任务跑起来",还在表达:

  • 任务边界
  • 生命周期边界
  • 取消边界
  • 异常传播边界

所以协程的重点是:

不仅要能并发,还要能把并发任务管清楚。


6. 虚拟线程保留了阻塞语义

很多人第一次接触虚拟线程时,很容易说出一句话:

"虚拟线程让阻塞调用变成非阻塞了。"

这个说法并不准确。

拿我 Java 示例里的代码来说:

这仍然是阻塞调用。

语义没有改变。

更准确的说法应该是:

虚拟线程没有改变阻塞语义,它只是把阻塞的代价大幅降低了。

也就是说:

  • Thread.sleep() 还是阻塞
  • BlockingQueue.take() 还是阻塞
  • JDBC 查询还是阻塞
  • Retrofit 的 execute() 还是阻塞

只是这些阻塞如果发生在虚拟线程里,JVM 可以把虚拟线程从 carrier thread 上卸下来,让底层平台线程去干别的活。

所以:

  • 阻塞没有消失
  • 只是不会再昂贵地占住平台线程

而 Kotlin 协程这边,很多操作在语义上就是"挂起"而不是"阻塞线程",这就是两者很本质的差异。


7. 结构化并发:Kotlin 协程真正稍强

如果只是发两个并发请求,你可能会觉得两者差不多。

但一旦业务复杂起来,差距就会慢慢显现。

比如:

  • 一个子任务失败了怎么办
  • 页面关闭时任务怎么取消
  • 超时后其他任务要不要停
  • 任务是不是属于同一个生命周期

Kotlin 协程从设计上就强调结构化并发。

像我 Kotlin 示例里的:

这意味着子任务都属于这个作用域,生命周期边界非常清晰。

而 Java 虚拟线程本身并不天然等于结构化并发。

Java 虽然也有 StructuredTaskScope,但那是另一层 API,不等于"只要用了虚拟线程,就自动拥有协程式的结构化语义"。

所以这部分可以总结成一句话:

虚拟线程偏执行模型,协程偏任务治理模型。


8. Android 上怎么选

这一部分基本是最现实的答案。

8.1 当前 Android 的主流答案仍然是协程

原因很简单,因为 Android 生态已经围绕协程形成了完整配套:

  • viewModelScope
  • lifecycleScope
  • Flow
  • Room
  • Paging
  • WorkManager

也就是说,在 Android 里,协程不是一个孤立的并发工具,而是一整套工程能力的一部分。

所以如果你今天在写 Android 项目,默认选择仍然应该是:

优先使用 Kotlin 协程。


8.2 Android 目前还没有真正实现虚拟线程

截至 目前,Android 官方 Thread 文档中 isVirtual() 的说明明确指出:

This method always returns false because virtual thread isn't implemented on Android yet.

这句话非常关键,说明两点:

  1. Android API 层已经出现了相关接口
  2. 但 Android 运行时当前还没有真正实现虚拟线程

所以如果有人问:

"Android 现在能不能直接像标准 JDK 21 一样用 Loom 虚拟线程?"

答案是:

还不能。


9. Android 的未来会怎样

这部分不适合说得太满,但可以做一个稳妥判断。

9.1 Android 不会完全忽略 Loom

既然已经有 isVirtual() 这样的 API 入口,就说明 Android 至少在接口层面已经考虑了兼容问题。

这意味着 Loom 不是一个完全与 Android 无关的方向。


9.2 但中短期内,协程地位依然很稳

因为现在 Android 的上层生态已经深度绑定协程:

  • 官方推荐方案是协程
  • Jetpack 组件大量围绕协程设计
  • 真实项目中,协程已经是主流并发抽象

所以更合理的判断不是"协程会不会被虚拟线程替代",而是:

未来 Android 可能逐步吸收 Loom 能力,但上层主要并发抽象很可能依旧是协程。

也就是说,未来最可能出现的是"分层协同",而不是"二选一替换"。


10. 该怎么选

如果你是 Java 服务端开发者,而且项目里已经有大量阻塞式调用:

  • JDBC
  • 同步 HTTP Client
  • 传统 DAO/Service 结构
  • 线程池 + Future 模型

那么虚拟线程是非常值得关注的选择,因为它迁移成本低、收益直接。

如果你是 Kotlin / Android 开发者,尤其在做:

  • 页面生命周期管理
  • UI 状态更新
  • Flow 数据流
  • ViewModel + Repository 架构

那么协程仍然是更自然、更成熟的方案。


11. 总结

回到我项目里的两份代码:

它们已经很好地说明了这件事:

相同点

  • 都适合并发 I/O
  • 都能让代码比传统回调更清晰
  • 都能提升高并发场景下的吞吐能力

不同点

  • Java 虚拟线程:让阻塞式代码重新变轻
  • Kotlin 协程:让异步任务从一开始就更结构化、更可管理

Android 未来

  • 截至目前,Android 仍未真正实现虚拟线程
  • 当前 Android 主流并发方案仍然是协程
  • 未来更可能是 Loom 能力逐步下沉,而协程继续作为上层主要抽象存在

GitHub

相关推荐
白云LDC11 小时前
Android Studio新建Vecter asset一直显示Loading icons(转圈圈)的解决办法
android·ide·android studio
Rytter14 小时前
某气骑士 libtprt.so 反 Frida 机制分析与绕过
android·安全·网络安全
alexhilton15 小时前
揭密:Compose应用如何做到启动提升34%
android·kotlin·android jetpack
沐言人生17 小时前
React Native 源码分析1——HybridData 机制深度分析
android·react native
程序员陆业聪17 小时前
跨平台框架全景图:Flutter/KMP/KuiKly/RN的2026年格局
android
码云数智-园园19 小时前
Fibers(纤程)来了:打破阻塞,实现纯PHP下的异步非阻塞IO
android
shaoming377621 小时前
检查系统硬件配置是否满足PyCharm最低要求
android·spring boot·mysql
一起搞IT吧1 天前
高通Camx功能feature分析之十五:insensor zoom介绍及实现
android·智能手机·相机
aqi001 天前
一文读懂 HarmonyOS 6.1 带来的十大重要升级
android·华为·harmonyos·鸿蒙·harmony