Java线程与Kotlin协程的关系与区别

5 阅读2分钟

关系

协程与线程本质是依附关系,协程并不能脱离线程独立存在,它底层是基于Java线程池的并发框架。协程本身只是一个轻量级的任务(task),最终执行权依然交由底层的CoroutineDispatcher分发给JVM线程来完成。

区别

它们区别,可以从三个方向展开说”资源模型“,”执行机制“,”切换成本“。

资源模型(内核态 VS 用户态)

  • Java线程属于操作系统,内核态线程,非常‘重’。创建一个线程通常需要1MB的栈内存,大量创建容易引发OOM.
  • kotlin协程是用户态的轻量级实体,在内存中本质只是一个Continuation对象,只占几十kb。

执行机制(阻塞VS挂起)

  • 在IO,CPU等耗时操作中,Java线程采用的是“阻塞(Blocking)”机制,会导致当前线程挂起,并不会释放资源,CPU资源会白白浪费。
  • 协程采用“非阻塞的挂起与恢复(supend & Resume)”机制。在耗时任务操作中,协程会主动交出执行权(挂起),但不会阻塞其所在的底层线程。底层线程会被立刻释放,去调度执行其他协程。这极大地提升了单线程的吞吐量。

切换成本(OS调度 vs 状态机)

  • “Java 线程的切换需要操作系统的参与,涉及昂贵的线程上下文切换(Context Switch) ,包含寄存器状态的保存和恢复,性能开销很大。
  • 协程的切换完全在用户态完成。Kotlin 编译器通过 CPS(Continuation-Passing Style,后续传递风格)变换,将 suspend 函数编译成了状态机(State Machine) 。协程的挂起和恢复,本质上只是内存中状态机 label 状态的流转,几乎没有性能损耗。”
维度Java 线程 (Thread)Kotlin 协程 (Coroutine)
层级操作系统级 (内核态)语言级 (用户态)
内存开销大 (约 1MB)极小 (数十 KB,纯对象)
耗时处理阻塞底层执行 (Blocking)挂起自身,不阻塞底层 (Suspend)
切换机制OS 上下文切换 (开销大)编译器 CPS 变换 / 状态机 (开销极小)