
Bun的创始人Jarred Sumner 最近在社交平台上说了一句话 "我还是想要多线程JavaScript"
这不是他第一次提这个想法 早在2024年5月 他就发起过一个投票 问大家觉得共享内存的多线程JS和AGI 哪个会先实现 结果45%的人选了多线程JS AGI只拿到32% 两千多票 浏览量两万多
这个数字很有意思 JS多线程这个话题讨论了快二十年 热度一直不减 期待值甚至压过了AGI

很多人可能会疑惑 Node.js不是早就有worker_threads了吗 浏览器也有Web Worker 这还不够吗
Jarred的回复很直接 "worker_threads根本不是那回事"
现在的Worker方案 本质上是多进程思路披了个多线程的外衣 每创建一个Worker 底层会生成一个独立的V8 Isolate 有自己的堆内存 自己的垃圾回收器 自己的全局对象
主线程和Worker线程之间完全隔离 想传数据只能靠postMessage 背后用的是结构化克隁算法 数据被完整复制一份传过去 传一个10MB的对象 实际消耗20MB内存 还多了序列化和反序列化的开销

这带来几个现实问题 每个Worker在Node.js里动辄消耗30MB以上 开20个Worker 光线程本身就吃掉几百MB 通信成本高 频繁通信的场景下性能损耗明显 状态无法共享 两个线程不能直接操作同一个对象
这哪是多线程 更像两个独立的JS进程在靠消息队列通信
Jarred想要的是什么样的?
他给出了具体描述 一个全局对象 所有线程共用同一个堆 一个垃圾回收器 统一管理内存 对象可跨线程访问 线程A创建的对象 线程B可以直接读写 不需要复制

没有postMessage开销 线程间通信不用序列化 直接操作共享内存里的对象 单线程内存占用小于2MB 可以轻量地开大量线程
这基本上就是Java、Go、C++的线程模型 在这些语言里 多个线程天然共享同一块堆内存 直接把对象引用传给另一个线程 对方立刻就能操作 没有任何拷贝
从性能角度看 这才叫多线程

既然好处这么明显 为什么JS一直做不到
答案藏在V8引擎的设计里 V8用Isolate作为独立运行单元 每个Isolate拥有自己的堆和GC 彼此完全隔离
这个设计的初衷是安全和简单 单线程模型下不需要考虑并发访问冲突 GC可以在没有外部干扰的情况下自由回收内存
要打破这个边界 最大的障碍是内存安全 多线程共享内存最头疼的是竞态条件 两个线程同时修改同一个对象 可能导致数据损坏甚至程序崩溃
Rust用所有权模型在编译期杜绝了这类问题 但JavaScript是动态语言 没有类型系统保障 静态检查很难做
另一个难点是GC 传统GC假设自己独占堆内存 标记对象时不希望别的线程在旁边改动 多线程共享堆之后 GC要么频繁暂停所有线程 要么实现复杂的并发GC 工程难度都不小
这就是为什么SharedArrayBuffer 是目前JS标准里最接近真共享内存的东西 但它只能存原始字节 不能存对象 局限很大
Node.js受制于V8的Isolate架构 想改线程模型得跟V8团队一起动 牵一发动全身
Bun不一样 它从底层重新实现了JavaScript运行时 用的是JavaScriptCore而不是V8 Bun对自己运行时架构的控制权 比Node.js大得多
这意味着Bun有能力 在不触动V8的情况下 去实验一种新的线程模型

TC39有个叫Shared Structs的提案正在推进 允许定义可在多个JS线程之间共享的结构体 配合Mutex和Condition来解决并发访问问题 但这个方案离Jarred描述的 "普通JS对象随意跨线程共享"还有距离
如果有一天JS真的实现了共享多线程 Node.js的编程模型会发生根本变化 图像处理、加密运算、数据解析这类CPU密集型工作 都能用纯JS线程高效并行
更直接的影响是内存效率 10个Worker现在要多用几百MB内存 真正的共享线程模型下 同样10个线程的额外开销可能只有20MB不到
JavaScript的单线程模型 帮它避开了几十年的并发地雷 代价是在CPU密集型场景上始终留着一块短板
Jarred的愿望不是第一次说 但这次背后多了Bun这几年积累的工程实力 以及他对运行时底层的掌控权
JS的多线程时代 也许真的比我们想象的要近一些 你怎么看 欢迎评论区聊聊