协程:轻量级并发的新范式
大学面试时,被问及 “协程” 却答成 “携程” 的经历,至今想来仍觉有趣。但协程作为轻量级并发的核心技术,在高并发场景中扮演着越来越重要的角色。本文将从协程的本质、与线程的区别,以及 Java 中如何通过框架使用协程展开讨论。
协程的核心概念
协程(Coroutine)是一种用户态的轻量级线程,由程序自身而非操作系统调度。它的出现源于对更高并发度的追求:
- 线程虽比进程轻量,但上下文切换仍需陷入内核态(保存寄存器、栈等状态),开销较大;
- 协程的切换完全在用户态完成,无需内核参与,开销仅为线程的几十分之一,支持百万级甚至千万级并发。
协程与线程的核心区别
| 特性 | 线程(Thread) | 协程(Coroutine) |
|---|---|---|
| 调度者 | 操作系统内核 | 用户程序(如框架或开发者控制) |
| 切换开销 | 大(内核态切换,涉及 CPU 寄存器、内存等) | 小(用户态切换,仅保存局部状态) |
| 并发量级 | 数千级(受内存和调度开销限制) | 百万级甚至千万级 |
| 阻塞影响 | 线程阻塞会导致 CPU 切换到其他线程 | 协程阻塞时可主动让出 CPU,不影响其他协程 |
Java 中的协程:Kilim 框架
Java 原生并未直接支持协程,但可通过第三方框架实现,Kilim是其中的经典代表。它通过字节码增强技术实现协程的暂停与恢复,核心类包括Task、Fiber和Mailbox。
引入 Kilim
在 Maven 项目中添加依赖:
1 | <dependency> |
Kilim 的核心组件
Task:协程的载体,类似线程的
Runnable,通过execute()方法启动协程。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import kilim.Task;
public class MyCoroutine extends Task {
public void execute() throws Exception {
System.out.println("协程执行中:" + Thread.currentThread().getName());
// 模拟耗时操作,可主动暂停
pause(); // 暂停协程,让出CPU
System.out.println("协程恢复执行");
}
public static void main(String[] args) throws Exception {
MyCoroutine coroutine = new MyCoroutine();
coroutine.start(); // 启动协程
coroutine.resume(); // 恢复协程
}
}Fiber:管理协程的执行状态,包括程序计数器(PC)、堆栈等,实现协程的暂停(
pause())与恢复(resume())。
核心属性:1
2
3
4
5
6public class Fiber {
public State curState; // 当前状态(运行、暂停等)
public int pc; // 程序计数器,记录执行位置
private State[] stateStack; // 状态堆栈,保存暂停时的上下文
private int iStack; // 栈指针
}Mailbox:协程间的通信工具,类似消息队列,支持协程间安全传递数据,避免共享内存的线程安全问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18import kilim.Mailbox;
public class MailboxExample extends Task {
private Mailbox<String> mailbox;
public MailboxExample(Mailbox<String> mailbox) {
this.mailbox = mailbox;
}
public void execute() throws Exception {
// 发送消息
mailbox.put("Hello from Coroutine");
// 接收消息
String msg = mailbox.get();
System.out.println("收到消息:" + msg);
}
}
协程的状态管理
Kilim 中,协程的状态通过Fiber的curState维护,主要包括:
- RUNNING:正在执行;
- SUSPENDED:暂停(主动调用
pause()或等待Mailbox消息); - FINISHED:执行完成;
- DEAD:异常终止。
状态切换由用户态调度器控制,无需内核参与,因此切换效率极高。
协程的适用场景
协程并非银弹,其优势在特定场景中才能充分发挥:
- I/O 密集型任务:如网络请求、文件读写等。协程在等待 I/O 时可主动让出 CPU,避免线程阻塞导致的资源浪费。
例:高并发 HTTP 服务,使用协程处理大量请求,性能远超线程池。 - 高频任务调度:如游戏引擎中的角色行为、实时数据处理等,协程的低延迟切换可保证任务响应速度。
- 简化异步代码:传统异步编程(如回调、Future)易导致 “回调地狱”,协程可通过同步代码风格编写异步逻辑,提升可读性。
Java 生态的其他协程方案
除 Kilim 外,Java 生态中还有其他成熟的协程框架:
- Project Loom:JDK 官方正在推进的协程方案(预览版已在 JDK 19 + 中提供),引入
VirtualThread(虚拟线程),本质是由 JVM 调度的协程,无需字节码增强,与现有 API 兼容。 - Quasar:另一个经典协程库,支持 JDK 8+,提供更丰富的调度策略和工具类。