1. 引言
在实际开发中,我们经常需要让程序“同时”做多件事。实现这一点的方式有多种,而最核心的手段之一就是多任务处理(multitasking)。
✅ 多任务处理:指多个任务并发执行,各自完成不同的工作,共享内存和资源,但逻辑上并行运行。
虽然这些任务看似“同时”运行,但实际上可能是由操作系统或 JVM 调度,在单核或多核 CPU 上快速切换实现的并发效果。关键在于,我们要让程序具备处理并发的能力。
2. 原生线程(Native Threads)
Java 中实现并发的标准方式是使用线程(Thread),而 Java 所使用的正是操作系统级别的“原生线程”。
⚠️ 原生线程直接映射到操作系统的执行单元(即 OS Thread),由操作系统负责调度到 CPU 核心上运行。这意味着:
- 线程的创建、切换、销毁都依赖 OS 支持
- 性能高,尤其适合长时间运行的任务
- JVM 无需自己管理线程调度底层细节
✅ 自 Java 1.2 起,JVM 全面采用原生线程模型,无论运行在 Windows、Linux 还是 macOS 上,这一机制保持一致。
也就是说,只要你用了以下任意一种标准并发工具,你就在使用原生线程:
java.lang.Thread
java.util.concurrent.Executor
java.util.concurrent.ExecutorService
ForkJoinPool
CompletableFuture
(底层依赖线程池)
💡 小结:原生线程性能好、支持广,是目前 Java 并发的基石。但它的“昂贵”之处在于——每个线程都会占用系统资源(栈内存、内核数据结构等),频繁创建销毁会带来显著开销。
3. 绿色线程(Green Threads)
绿色线程是一种用户态线程,它不直接对应操作系统线程,而是由运行时环境(如虚拟机或框架)在少数几个原生线程上模拟出多个逻辑线程。
工作原理简单来说就是:
多个绿色线程 → 映射到少量原生线程 → 由运行时系统调度执行
优势 ✅
- 创建/销毁成本极低
- 数量可成千上万(不像原生线程受限于系统资源)
- 更适合短生命周期任务(比如处理大量 HTTP 请求)
劣势 ❌
- 无法真正并行(只能在一个原生线程上轮流跑)
- 容易因某个任务阻塞导致整体卡住
- 需要运行时深度介入调度逻辑
⚠️ Java 曾经在早期版本(1.1 及之前)使用绿色线程作为默认模型,但从 Java 1.2 开始彻底转向原生线程,至今 JVM 层面不再支持绿色线程。
虽然 JVM 没有内置支持,但一些第三方库通过字节码增强或 Java Agent 实现了类似能力——比如我们接下来要讲的 Fibers。
4. Fiber(纤程)
Fiber 是一种轻量级并发单元,概念上接近绿色线程,但关键区别在于:它是协作式(cooperative)而非抢占式(preemptive)的多任务模型。
对比项 | 原生线程 / 绿色线程 | Fiber |
---|---|---|
调度方式 | 抢占式(系统决定何时切换) | 协作式(代码主动 yield) |
切换控制 | OS 或运行时强制中断 | 开发者显式声明可暂停点 |
上下文开销 | 高(内核态切换) | 极低(用户态保存栈) |
并发密度 | 数千级 | 数十万级 |
✅ Fiber 的核心思想是:“我运行到某个安全点时,自己说一声‘我可以让出’”,而不是被系统强行打断。
⚠️ 如果你的 fiber 代码中没有 yield 点,那它就会一直霸占线程,其他 fiber 根本没机会执行 —— 所以必须配合非阻塞 I/O 和显式挂起机制使用。
目前 Java 语言本身并未原生支持 Fiber,但我们可以通过以下几种方式引入:
4.1. Quasar
Quasar 是一个成熟的 Java/Kotlin 协程库,支持:
- Java 11+(推荐),旧版支持 Java 8
- 使用 Java Agent 在运行时织入 fiber 支持
- 无需修改构建流程,启动时加
-javaagent:quasar.jar
即可
示例代码:
public class FiberExample {
public static void main(String[] args) throws Exception {
Fiber<Integer> fiber = new Fiber<>(() -> {
System.out.println("Fiber 开始执行");
Fiber.sleep(1000);
return 42;
}).start();
System.out.println("主线程继续");
System.out.println("Fiber 结果: " + fiber.get());
}
}
✅ 优点:API 简洁,与 Kotlin 协程兼容性好
❌ 缺点:依赖 Java Agent,某些环境受限(如容器安全策略限制 agent 加载)
4.2. Kilim
Kilim 是另一个轻量级 fiber 实现,与 Quasar 最大的不同在于:
它使用 字节码织入(bytecode weaving) 而非 Java Agent。
这意味着:
- 在编译期修改 class 文件,插入调度逻辑
- 运行时不依赖 agent,更适合生产环境
- 支持 Java 7+
示例任务定义:
public class MyTask extends Task {
public void run() throws Pausable {
System.out.println("Task 开始");
pause(); // 主动挂起
System.out.println("Task 恢复");
}
}
✅ 适用场景:不能用 agent 的项目、对运行时纯净性要求高的系统
⚠️ 踩坑提示:构建流程需集成 weave 插件(如 Maven 插件),配置稍复杂
4.3. Project Loom
Project Loom 是 OpenJDK 官方推动的一个实验性项目,目标是将 Fiber 直接集成进 JVM,提供原生级别的轻量级并发支持。
核心特性包括:
- 新增
java.lang.Thread
的新实现模式(虚拟线程) - 兼容现有 Thread API,无需重写代码
- 不再需要 Java Agent 或字节码织入
- 极大提升并发吞吐量(单机支持百万级并发)
🔔 注意:截至 JDK 21,Loom 已进入预览阶段(Preview Feature),可通过启动参数开启:
--enable-preview --source 21
示例代码(虚拟线程):
public class VirtualThreadExample {
public static void main(String[] args) {
Thread.startVirtualThread(() -> {
System.out.println("我在虚拟线程中运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {}
System.out.println("虚拟线程结束");
});
}
}
✅ 展望未来:Loom 很可能成为 Java 下一代并发模型的基础,一旦正式发布,将彻底改变我们编写高并发服务的方式。
5. 协程(Coroutines)
协程可以理解为“没有调度器的 fiber”——它的执行流程完全由调用方控制。
✅ 协程本质是一段可暂停、可恢复的函数,yield 时保存状态,下次 resume 时从中断处继续。
典型使用模式如下:
import kotlinx.coroutines.*
fun main() = runBlocking {
launch {
for (i in 1..3) {
print("协程输出 $i ")
yield() // 主动让出
}
}
repeat(3) {
println("主流程工作")
delay(100)
}
}
输出可能为:
协程输出 1 主流程工作
协程输出 2 主流程工作
协程输出 3 主流程工作
📌 关键点:
- Kotlin 原生支持协程(
kotlinx.coroutines
) - Java 没有语言级支持,但可通过 Quasar 等库模拟
- 更适合流程控制类场景(如状态机、生成器),而非大规模并发 I/O
对比总结:
特性 | 原生线程 | Fiber | 协程 |
---|---|---|---|
调度方式 | 抢占式 | 协作式 | 手动控制 |
并发规模 | 数千 | 数十万 | 中等 |
使用难度 | 简单 | 中等 | 中等 |
是否需要特殊支持 | 否 | 是(Agent/Weave/JVM 改造) | 是(语言或库) |
6. 总结
Java 的并发演进路径清晰可见:
从原生线程 → 第三方 fiber 库(Quasar/Kilim) → JVM 原生支持(Project Loom)→ 协程补充控制流
📌 当前建议:
- ✅ 普通业务:继续使用
ExecutorService
+ 原生线程池 - ✅ 高并发 I/O 密集型服务:关注 Project Loom 虚拟线程,尽早试用预览版
- ✅ 流程控制复杂逻辑:考虑 Kotlin 协程或 Quasar
- ❌ 不要尝试自己实现绿色线程或 fiber 调度器,容易踩坑且性能难保障
🔥 下一步行动:下载 JDK 21+,开启
--enable-preview
,亲手体验一把百万并发的丝滑感。