一、Dispatchers是如何工作的?
概述:
CoroutineContext 是容器,ContinuationInterceptor 是其中的关键元素接口,CoroutineDispatcher 是该接口的主要实现,而 Dispatchers 是预定义调度器的工具类。

1.CoroutineContext
CoroutineContext 是一个不可变的元素集合,每个元素都实现了 Element
接口。它的核心作用是存储和传递协程的上下文信息(如调度器、Job、异常处理器等)。
Kotlin
public interface CoroutineContext {
// 通过Key获取上下文中的元素
public operator fun <E : Element> get(key: Key<E>): E?
// 合并两个上下文
public operator fun plus(context: CoroutineContext): CoroutineContext = ...
// 遍历上下文中的所有元素
public fun <R> fold(initial: R, operation: (R, Element) -> R): R
// 其他方法...
// 上下文中元素的Key接口
public interface Key<E : Element>
// 上下文元素的基接口
public interface Element : CoroutineContext {
public val key: Key<*>
}
}
- CoroutineContext 是一个接口,其默认实现是
CombinedContext
(链表结构)或EmptyCoroutineContext
。 - 每个元素都有唯一的
Key
,用于快速查找和替换。
2.ContinuationInterceptor
ContinuationInterceptor 是 CoroutineContext 中的一种特殊元素,负责拦截协程的执行并改变其调度方式。
Kotlin
public interface ContinuationInterceptor : CoroutineContext.Element {
companion object Key : CoroutineContext.Key<ContinuationInterceptor>
// 拦截并返回一个新的Continuation
public fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T>
}
它的唯一实现是 CoroutineDispatcher
,即调度器是拦截器的具体实现。通过 interceptContinuation
方法,可以在协程恢复执行时插入自定义逻辑(如线程切换)。
3.CoroutineDispatcher
CoroutineDispatcher 继承自 ContinuationInterceptor,负责决定协程代码块在哪个线程或线程池执行。
Kotlin
public abstract class CoroutineDispatcher : ContinuationInterceptor {
override val key: CoroutineContext.Key<*> get() = ContinuationInterceptor
// 判断是否需要调度(即是否需要切换线程)
public open fun isDispatchNeeded(context: CoroutineContext): Boolean = true
// 将任务分派到指定线程执行
public abstract fun dispatch(context: CoroutineContext, block: Runnable)
// 拦截协程的恢复操作,实现线程切换
override fun <T> interceptContinuation(continuation: Continuation<T>): Continuation<T> =
DispatchedContinuation(this, continuation)
}
dispatch
方法是核心,不同的调度器(如 Main、IO、Default)通过不同的实现决定任务执行位置。DispatchedContinuation
是一个包装类,负责在协程恢复时调用调度器的dispatch
方法。
Dispatchers 是一个单例对象,提供常用的调度器实例,方便开发者直接使用。
Kotlin
public actual object Dispatchers {
// 主调度器(用于UI线程,如Android的MainLooper)
public actual val Main: CoroutineDispatcher get() = MainDispatcherLoader.dispatcher
// 默认调度器(用于CPU密集型任务,默认使用JVM的公共线程池)
public actual val Default: CoroutineDispatcher = createDefaultDispatcher()
// IO调度器(用于IO密集型任务,使用弹性线程池)
public actual val IO: CoroutineDispatcher = DefaultIoScheduler
// 未受限调度器(立即在当前线程执行,直到第一个挂起点)
public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined
}
- 这些调度器都是 CoroutineDispatcher 的具体实现。
- 通过
Dispatchers
可以快速获取预定义的调度器,也可以自定义调度器。
阶段总结:
Kotlin协程调度机制的工作流程为:首先通过`Dispatchers`工具类获取预定义的`CoroutineDispatcher`实例(如`Dispatchers.IO`),并与其他上下文元素(如`Job`)组合成`CoroutineContext`;启动协程时,协程构建器(如`launch`)将此上下文与协程绑定,其中的`ContinuationInterceptor`(由`CoroutineDispatcher`实现)会拦截协程的`Continuation`,注入调度逻辑;
调度器通过`isDispatchNeeded()`判断是否需要切换线程,若需要则调用`dispatch()`将任务提交到目标线程/线程池(如IO线程池或UI线程);协程执行到挂起点时保存状态暂停执行,恢复时再次由调度器判断并处理线程切换;
协程结束后,相关资源(如线程池中的空闲线程)会被自动清理,整个过程通过上下文和调度器的协同工作,实现了协程在不同执行环境间的高效切换与管理。
整体关系图:
Kotlin
CoroutineContext (容器)
├─ Job (协程的生命周期管理)
├─ CoroutineName (协程名称)
└─ ContinuationInterceptor (拦截器接口)
└─ CoroutineDispatcher (调度器实现)
├─ Dispatchers.Main (UI线程调度器)
├─ Dispatchers.Default (默认调度器)
├─ Dispatchers.IO (IO调度器)
└─ Dispatchers.Unconfined (未受限调度器)
二、CoroutineScope是如何管理协程的?
1. CoroutineScope 的创建:强制绑定 Job 元素
CoroutineScope
的核心特性是确保其coroutineContext
中必然存在 Job 元素 ,这是管理协程的基础。源码中,CoroutineScope
的创建通常通过两种方式保证 Job 存在:
- 显式传入包含 Job 的上下文,如
CoroutineScope(Job() + Dispatchers.Main)
; - 若传入的上下文不含 Job,会自动添加默认 Job(如
GlobalScope
的实现中,coroutineContext
默认包含EmptyCoroutineContext + Job()
)。
其接口定义明确依赖coroutineContext
,而 Job 作为上下文的强制元素,成为 Scope 管理协程的 "根节点":
Kotlin
public interface CoroutineScope {
public val coroutineContext: CoroutineContext // 必然包含Job
}
2. 协程创建:构建 Job 的 N 叉树结构
当通过launch
、async
等构建器(CoroutineScope
的扩展函数)创建协程时,会触发以下源码逻辑,形成 Job 的层级关系:
- 创建协程实例 :构建器会实例化
AbstractCoroutine
的子类(如StandaloneCoroutine
、DeferredCoroutine
),这些子类是协程的具体载体,内部持有一个Job
实例(子 Job)。 - 绑定父子 Job :在
AbstractCoroutine
的initParentJob()
方法中,会将子 Job 与CoroutineScope
的 Job(根 Job)或上级协程的 Job(父 Job)绑定,核心逻辑如下:
Kotlin
internal abstract class AbstractCoroutine<in T>(
override val context: CoroutineContext,
active: Boolean
) : JobSupport(active), Continuation<T>, CoroutineStackFrame {
init {
// 初始化父Job关系
initParentJob(context[Job])
}
private fun initParentJob(parent: Job?) {
if (parent != null) {
// 将当前Job(子Job)注册到父Job中,形成父子关联
parent.attachChild(this)
}
}
}
每个父 Job 会通过attachChild
维护一个子 Job 列表,最终所有协程的 Job 以CoroutineScope
的根 Job 为顶点,形成N 叉树结构(根 Job→子 Job→孙 Job...)。
- 取消与异常传播:基于树结构的递归机制
Job 的 N 叉树结构决定了取消事件和异常的传播规则,源码中通过JobSupport
(Job 的核心实现类)的事件通知机制实现:
-
父 Job 取消:向下递归取消所有子 Job
当
CoroutineScope
的根 Job 被取消(如scope.cancel()
),会触发JobSupport.cancel()
方法,该方法会遍历所有子 Job 并调用其cancel()
,形成递归:
Kotlin
internal open class JobSupport(...) : Job {
override fun cancel(cause: CancellationException?) {
// 标记当前Job为取消状态
val token = markCancelled(cause)
if (token != null) {
// 遍历所有子Job,触发取消
cancelChildren(cause, token)
// 通知父Job当前Job已取消(向上传播)
parent?.childCancelled(this, cause)
}
}
}
子 Job 异常:区分异常类型的双向传播
当子 Job 发生异常时,传播方向取决于异常类型:
这种传播机制本质是责任链模式的体现:每个 Job 既是子 Job 的管理者(处理向下传播),又是父 Job 的责任节点(处理向上传播),确保异常能沿着树结构扩散至整个 Scope。
- 若为
CancellationException
(主动取消):仅向下传播(取消当前 Job 的所有子 Job),父 Job 会忽略该异常(源码中childCancelled
方法对CancellationException
直接返回false
,不触发父 Job 取消)。 - 若为其他异常 (如
IOException
):先向下取消所有子 Job,再向上传播至父 Job,父 Job 接收异常后会触发自身取消,并继续向上传播,最终导致根 Job(CoroutineScope
的 Job)取消,所有关联协程终止。
阶级总结:
CoroutineScope
管理协程的核心在于其coroutineContext
中必然存在的Job
元素,该元素作为根节点,在协程创建时通过launch
、async
等构建器实例化AbstractCoroutine
子类,这些子类在initParentJob()
方法中会将自身持有的子Job
与根Job
或上级协程的父Job
绑定,形成以根Job
为顶点的 N 叉树结构;
当取消事件或异常发生时,基于该树结构和JobSupport
的事件通知机制,CancellationException
仅向下传播取消当前Job
的所有子Job
且父Job
会忽略该异常,而其他异常则先向下取消所有子Job
,再通过责任链模式向上传播至父Job
,最终可能导致根Job
取消,从而实现对协程的结构化管理。