Skip to content

跨语言运行时并发总对照:Java / Kotlin / Dart / JS / Go ​

所属模块:并发与运行时底座 README
建议前置:运行时承载模型入口 · Java 并发模型总览 · Kotlin 协程并发总览 · Dart 并发总览:Future / event loop / isolate · JS Event Loop / Promise / Worker · Go goroutine / channel / context

一句话定位 ​

这页不是讲百科,而是给 Android 背景、正在补 Flutter/Dart 体系的工程师 一张跨语言判断表:当你看到 Thread、coroutine、Future、Promise、goroutine、isolate、Worker 时,先别问“语法怎么写”,先问 谁在承载执行、挂起后现场放哪、共享状态怎么处理、取消和背压怎么治理。

代码索引 ​

语言主线主题页对应实验
JavaJava 并发模型总览thread-pool-basics · volatile-vs-atomic-vs-lock
KotlinKotlin 协程并发总览coroutine-execution-flow · coroutine-mutex-stateflow
Dart / FlutterDart 并发总览:Future / event loop / isolatedart-event-loop-isolate
JSJS Event Loop / Promise / Worker当前以理论文档为主,建议后续补 labs/node/runtime-concurrency/
GoGo goroutine / channel / context当前以理论文档为主,建议后续补 labs/go/runtime-concurrency/

为什么这页值得单独看 ​

如果你从 Android 转到 Flutter / Dart,最常见的误判不是语法不会,而是把 Java/Kotlin 的共享内存直觉 直接套到 Dart / JS / Go:

  • 把 Future 当成“后台线程句柄”
  • 把协程当成“更轻线程”就结束
  • 把 Promise 当成“不会卡 UI 的异步”
  • 把 goroutine 当成“随便开、总会自己收”的廉价线程
  • 把 isolate 想成“Dart 版协程”

这页要帮你建立的不是定义,而是以下工程判断:

  1. 当前问题是调度问题、共享状态问题,还是隔离问题?
  2. 我需要的是异步编排,还是需要真正把 CPU 重活挪出去?
  3. 当前模型主要靠共享内存,还是主要靠消息传递?
  4. 取消、超时、背压、生命周期治理在这个语言里靠什么落地?

推荐先看的五个对照维度 ​

1. 执行载体:代码到底由谁承载 ​

语言默认执行载体工程上怎么理解
JavaOS 线程 / 线程池线程任务最终一定落到真实线程上跑,线程数量、阻塞、切换成本都要管
Kotlin仍借 JVM 线程,协程本身不是线程协程是“任务描述 + 挂起恢复语义”,真正执行仍靠 Dispatcher 背后的线程
Dart当前 isolate 的 event loop同一 isolate 内默认不并行;大部分 Future 只是排队,不是开新线程
JS主线程 event loopPromise/async 先理解为“回调何时恢复”,不是线程切换
Gogoroutine,由 runtime 调度到少量 OS 线程任务很轻,但不是免费;阻塞、泄漏、背压设计不好一样出事

工程结论

  • Java / Kotlin 更像“多线程世界里如何安全组织任务”。
  • Dart / JS 更像“单执行主线里如何安排回调,并在必要时显式隔离重活”。
  • Go 是“轻量任务很多,但仍要对调度、退出和资源负责”。

2. 挂起与恢复:现场放哪、靠什么继续跑 ​

语言挂起/等待时现场主要放哪恢复机制
Java线程阻塞时现场仍主要在线程栈,Future 结果在堆对象里线程被唤醒、继续执行,或线程池里其他线程取到下一个任务
Kotlin编译器改写成状态机 + Continuation,关键局部状态转到堆对象Dispatcher 选择线程,Continuation 恢复后半段
Dartasync / await 后续变成 continuation,排回当前 isolate 队列event loop 在 Future 完成后继续恢复
JSPromise.then / await 后续进入 microtaskevent loop 清空 microtask 时恢复回调
Gogoroutine 被 runtime 挂起/阻塞,栈可增长收缩runtime 调度 goroutine 继续执行

工程结论

  • Kotlin 和 Dart/JS 都有“看起来顺序写,底层其实是恢复后半段”的特征,但 Kotlin 站在多线程调度器之上,Dart/JS 站在事件循环之上。
  • Java 的 Future 更像“结果句柄”;Kotlin/Dart/JS 的 suspend / await 更强调“把后半段恢复出来”。
  • Go 不强调 await 语法,而是靠 runtime 调度 goroutine + channel/context 组织等待与退出。

3. 共享状态模型:靠共享内存还是消息传递 ​

语言主模型共享状态风险点第一反应手段
Java共享内存可见性、竞态、锁竞争、错误发布synchronized / Lock / volatile / Atomic* / 并发容器
Kotlin共享内存 + 协程编排误以为“用了协程就线程安全”Mutex、受控 Dispatcher、StateFlow、结构化并发
Dartisolate 隔离,不共享普通对象主 isolate 被重活占住、消息复制成本isolate / Isolate.run / compute
JS主线程单共享执行线,Worker 走消息传递UI 长任务、回调顺序错觉Worker / 分块处理 / 限制 microtask 滥用
Go倾向“通过通信共享,而不是通过共享来通信”goroutine 泄漏、channel 阻塞、缓冲失控channel、context、明确退出协议

工程结论

  • Java / Kotlin 的难点是 共享状态怎么证明安全。
  • Dart / JS 的难点是 你以为自己异步了,其实重活还留在主执行线上。
  • Go 的难点是 任务很多时如何停得干净、背压设计是否健康。

4. 真正并发 vs 只是异步 ​

场景JavaKotlinDart / FlutterJSGo
发起一个异步 IO线程池任务 / 非阻塞框架回调suspend + withContext(IO) 等Future / awaitPromise / awaitgoroutine + IO / netpoll
让 UI/主执行线不被大计算卡住把任务移到后台线程池withContext(Default) 或专门线程池isolate / computeWorkergoroutine 通常已可并行,但仍要控制数量
只是把回调延后,不等于并行Future/回调句柄本身不等于并行协程本身不等于独占线程Future(() {}) 不等于新 isolatePromise 不等于新线程goroutine 常常真并发,但不等于无限扩张

工程结论

  • 异步 ≠ 并行 这条在 Dart / JS 最容易踩坑,在 Kotlin 次之,在 Java/Go 容易因为线程/GMP 存在而被误以为天然安全。
  • Flutter 卡顿场景里,先区分“网络慢”还是“解析/计算把 UI isolate 占住”。
  • Android 场景里,先区分“主线程阻塞”还是“后台线程池/锁竞争把结果拖慢”。

5. 取消、超时、生命周期治理 ​

语言核心治理手段最容易踩的坑
JavaFuture.cancel()、线程中断、线程池生命周期只会启动,不会停;中断约定不一致
KotlinCoroutineScope、Job、结构化并发、SupervisorJobGlobalScope 乱飞,页面销毁后协程还跑
Dart / Flutter手动协议、页面生命周期管理、isolate 生命周期以为 Future 天然跟页面生命周期绑定
JSAbortController、组件卸载清理、Worker 终止页面已离开但异步回调还回写状态
Gocontext、Done()、deadline、cancelgoroutine 泄漏、忘记 cancel()

工程结论

  • Kotlin 和 Go 在“取消传播”这件事上最成体系:一个靠 Job/作用域,一个靠 context。
  • Dart / JS 更需要工程师自己建立协议,不然最容易留下“页面没了,任务还在”的尾巴。
  • Java 能做取消,但体系化程度比 Kotlin / Go 更依赖约定和工程纪律。

一张总表:先拿它做面试或设计前的快速校准 ​

维度JavaKotlinDart / FlutterJS / WebGo
主执行模型多线程 + 共享堆多线程 + 协程挂起恢复单 isolate event loop单线程 event loopgoroutine + runtime 调度
最小任务语义Runnable / Callable / Futurelaunch / async / suspendFuture / asyncPromise / asyncgoroutine
真正并发单元Thread仍是线程isolateWorkergoroutine(映射到多线程)
共享数据方式共享内存共享内存isolate 间消息传递Worker 间消息传递channel / 共享内存都可,但鼓励通信优先
恢复机制线程继续执行 / 调度器唤醒Continuation + Dispatcherevent loop 恢复 continuationmicrotask/macrotask 恢复回调runtime 调度继续运行
常见重活迁移方式专门线程池Dispatchers.Default / 专门池Isolate.run / computeWorker限流后的 goroutine / worker pool
首要排障对象锁、线程池、JMM、竞态Scope、Dispatcher、取消传播、共享状态主 isolate 卡顿、队列顺序、isolate 成本主线程长任务、队列优先级、Worker 边界goroutine 泄漏、channel 堵塞、context 传播

Android 背景 Flutter 工程师最值得带走的迁移判断 ​

1. 从 Java/Kotlin 迁到 Dart 时,先扔掉“Future≈线程”直觉 ​

在 JVM 世界里,你很容易把“异步”联想到线程池、后台线程、锁和共享状态;但到 Dart:

  • Future 往往只是 当前 isolate 里的调度
  • await 是 恢复后半段,不是“切线程回来”
  • 只有 isolate 才是“真正把 CPU 重活移出去”的手段

对应主线:Dart 并发总览:Future / event loop / isolate

2. 从 Kotlin 协程迁到 Flutter 时,要保留“结构化治理”习惯,但别照搬实现心智 ​

Kotlin 协程让你习惯:

  • Scope 绑定生命周期
  • Job 负责取消传播
  • Dispatcher 决定在哪恢复

到 Flutter 后,这些好习惯仍然重要,但落地手段不同:

  • Flutter 没有协程式 Job / Scope 体系替你兜底
  • 很多取消和退出约束要你自己设计
  • isolate 启动、消息传递、回收协议是另一套成本模型

对应主线:Kotlin 协程并发总览 · Dart isolate / Flutter compute:真正的并发隔离

3. JS 和 Dart 很像,但别忽略 Flutter 宿主约束 ​

JS 与 Dart 都有 event loop / microtask 心智,所以:

  • Promise vs Future
  • microtask vs Future.microtask
  • Worker vs isolate

这些对照很顺手。但 Flutter 比纯 Web 多了:

  • UI isolate 和渲染/平台通道协作边界
  • 页面生命周期、宿主线程、插件桥接
  • 大对象序列化与平台切换成本

所以不能只用前端脑补 Flutter 卡顿问题。

对应主线:JS Event Loop / Promise / Worker

4. Go 最值得借鉴的不是语法,而是“停得干净”的治理意识 ​

很多移动端工程师看 Go,只看到:

  • goroutine 很轻
  • channel 很方便

真正值得迁移回移动端工程心智的是:

  • 并发任务不是只看怎么发起,更要看怎么退出
  • timeout / cancel / backpressure 必须前置设计
  • 泄漏不是只发生在内存对象,也发生在“永远不退出的执行单元”

这对 Kotlin 协程取消治理、Flutter isolate 生命周期设计都很有借鉴价值。

对应主线:Go goroutine / channel / context

典型决策题:工程上怎么快速选 ​

题 1:Flutter 页面首帧卡顿,大 JSON 解析放哪? ​

题 2:Android 仓库层同时发多个请求并汇总,最该先想什么? ​

  • Java:线程池边界、阻塞等待位置、共享状态安全
  • Kotlin:coroutineScope / async、取消传播、结果汇总失败策略
  • 不要只停在“能并发发出去”

题 3:一个前端/H5 同学说“我已经用了 Promise,所以不会卡 UI”怎么办? ​

  • 先判断重活是否还在主线程
  • Promise 只改变调度顺序,不自动提供并行隔离
  • 真重活还是要看 Worker

题 4:一个 Go 服务 goroutine 越跑越多,移动端工程师该怎样类比理解? ​

  • 类比 Kotlin 里协程没绑生命周期、取消没传下去
  • 类比 Flutter 里 isolate 起了没收、消息通道一直悬挂
  • 本质都是“执行单元退出治理失败”

推荐复习顺序 ​

  1. 先读 运行时承载模型入口
  2. 再读本页,把五种语言放回同一张判断表
  3. 回到三条主线:
  4. 最后用 JS Event Loop / Promise / Worker 和 Go goroutine / channel / context 做边界校准

对应实验 ​

主题Lab说明
Java 线程池 / 结果句柄 / 队列行为thread-pool-basics · queue-strategy-compare对照“任务提交”和“真实线程治理”不是一回事
Java 共享状态与原子性volatile-vs-atomic-vs-lock对照共享内存世界里为什么仅异步不够
Kotlin 挂起恢复 / Dispatchercoroutine-execution-flow观察 Main → IO → Main 恢复链
Kotlin 取消传播 / 共享状态保护coroutine-mutex-stateflow观察结构化并发与 Mutex 的不同职责
Dart event loop / isolatedart-event-loop-isolate对照“调度顺序”和“真正隔离并发”

复习检查题 ​

  1. 为什么说 Kotlin 协程和 Dart Future 都能“顺序写异步”,但它们的恢复地基不同?

    答:Kotlin 协程的挂起恢复建立在状态机 + Continuation + Dispatcher 之上,恢复时仍要借 JVM 线程执行;Dart Future / await 的恢复建立在当前 isolate 的 event loop 之上,后半段是被重新排队恢复的,不等于切到另一条线程。

  2. 为什么 Android 背景工程师迁到 Flutter 时,最容易误判 CPU 卡顿问题?

    答:因为容易把 JVM 世界里的“异步≈后台线程”直觉带到 Dart,把 Future 误当成独立执行单元。实际上很多 Future 仍在 UI isolate 内排队,CPU 重活如果不搬到 isolate,页面仍会卡顿。

  3. Go 的 context 与 Kotlin 的 Job 最值得类比的共同点是什么?

    答:两者都在解决任务树里的取消/超时传播,不只是“把任务启动起来”,而是让任务能沿调用链正确停下来。

  4. JS Promise、Dart Future、Java Future 这三个名字都叫 Future/Promise,为什么不能当成同一类东西来背?

    答:因为它们主要解决的问题不同:JS Promise 和 Dart Future 更强调事件循环里的异步结果编排与后半段恢复;Java Future 更像线程池/任务执行结果的句柄,本身不提供 JS/Dart 那种统一的事件循环恢复语义。

速记 ​

  • Java:共享内存并发,重点是线程、锁、JMM、线程池
  • Kotlin:线程之上的挂起恢复,重点是 Continuation、Dispatcher、Scope、Job
  • Dart:event loop + isolate,重点是 Future 不等于线程
  • JS:Promise 只是调度,不是并行;重活靠 Worker
  • Go:goroutine 很轻,但真正难点是 channel / context / 退出治理
  • 先问模型,再选 API

站点构建时间:2026/8/24 23:43:17