0%

协程

协程:轻量级并发的新范式

大学面试时,被问及 “协程” 却答成 “携程” 的经历,至今想来仍觉有趣。但协程作为轻量级并发的核心技术,在高并发场景中扮演着越来越重要的角色。本文将从协程的本质、与线程的区别,以及 Java 中如何通过框架使用协程展开讨论。

协程的核心概念

协程(Coroutine)是一种用户态的轻量级线程,由程序自身而非操作系统调度。它的出现源于对更高并发度的追求:

  • 线程虽比进程轻量,但上下文切换仍需陷入内核态(保存寄存器、栈等状态),开销较大;
  • 协程的切换完全在用户态完成,无需内核参与,开销仅为线程的几十分之一,支持百万级甚至千万级并发。

协程与线程的核心区别

特性 线程(Thread) 协程(Coroutine)
调度者 操作系统内核 用户程序(如框架或开发者控制)
切换开销 大(内核态切换,涉及 CPU 寄存器、内存等) 小(用户态切换,仅保存局部状态)
并发量级 数千级(受内存和调度开销限制) 百万级甚至千万级
阻塞影响 线程阻塞会导致 CPU 切换到其他线程 协程阻塞时可主动让出 CPU,不影响其他协程

Java 中的协程:Kilim 框架

Java 原生并未直接支持协程,但可通过第三方框架实现,Kilim是其中的经典代表。它通过字节码增强技术实现协程的暂停与恢复,核心类包括TaskFiberMailbox

引入 Kilim

官网地址:https://kilim.malhar.net/

在 Maven 项目中添加依赖:

1
2
3
4
5
<dependency>
<groupId>org.db4j</groupId>
<artifactId>kilim</artifactId>
<version>2.0.1</version>
</dependency>

Kilim 的核心组件

  • Task:协程的载体,类似线程的Runnable,通过execute()方法启动协程。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    import kilim.Task;

    public class MyCoroutine extends Task {
    @Override
    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
    6
    public 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
    18
    import kilim.Mailbox;

    public class MailboxExample extends Task {
    private Mailbox<String> mailbox;

    public MailboxExample(Mailbox<String> mailbox) {
    this.mailbox = mailbox;
    }

    @Override
    public void execute() throws Exception {
    // 发送消息
    mailbox.put("Hello from Coroutine");
    // 接收消息
    String msg = mailbox.get();
    System.out.println("收到消息:" + msg);
    }
    }

协程的状态管理

Kilim 中,协程的状态通过FibercurState维护,主要包括:

  • RUNNING:正在执行;
  • SUSPENDED:暂停(主动调用pause()或等待Mailbox消息);
  • FINISHED:执行完成;
  • DEAD:异常终止。

状态切换由用户态调度器控制,无需内核参与,因此切换效率极高。

协程的适用场景

协程并非银弹,其优势在特定场景中才能充分发挥:

  1. I/O 密集型任务:如网络请求、文件读写等。协程在等待 I/O 时可主动让出 CPU,避免线程阻塞导致的资源浪费。
    例:高并发 HTTP 服务,使用协程处理大量请求,性能远超线程池。
  2. 高频任务调度:如游戏引擎中的角色行为、实时数据处理等,协程的低延迟切换可保证任务响应速度。
  3. 简化异步代码:传统异步编程(如回调、Future)易导致 “回调地狱”,协程可通过同步代码风格编写异步逻辑,提升可读性。

Java 生态的其他协程方案

除 Kilim 外,Java 生态中还有其他成熟的协程框架:

  • Project Loom:JDK 官方正在推进的协程方案(预览版已在 JDK 19 + 中提供),引入VirtualThread(虚拟线程),本质是由 JVM 调度的协程,无需字节码增强,与现有 API 兼容。
  • Quasar:另一个经典协程库,支持 JDK 8+,提供更丰富的调度策略和工具类。

欢迎关注我的其它发布渠道