一、协程调度器与线程池 —— 从 Executor 到 Dispatcher

CoroutineScope 就是协程世界的 Executor,Dispatchers 就是线程策略的实现——从源码角度看懂调度器的继承体系与自定义线程池

CoroutineScope CoroutineDispatcher ContinuationInterceptor Executor 线程池

目录导航(点击跳转)

一、线程执行方式演进:从 Thread 到 CoroutineScope

1Java 原生方式:手动管理线程
// Java 中最原始的方式:手动创建 Thread
Thread {
    // 执行任务
    TODO()
}.start()

// Kotlin 提供了一个语法糖,底层还是创建 Thread
thread {
    TODO()
}
2线程池:Executor 统一管理

实际开发中不会每次都 new Thread——开销太大。线程池复用已有线程,大幅提升性能:

// 创建一个可缓存的线程池
val executor = Executors.newCachedThreadPool()
executor.execute {
    // 任务代码被包装成一个 Runnable 对象
    TODO()
}
3Android 特化:Handler 指定线程

在 Android 中,经常需要把任务切回主线程更新 UI:

// Handler:向主线程消息队列投递任务
val handler = Handler(Looper.getMainLooper())
handler.post {
    // 在主线程执行
}

// View.post:等价于上面的写法
val view: View = findViewById(R.id.xxx)
view.post {
    // 也在主线程执行
}
4协程的统一抽象:CoroutineScope

CoroutineScope 在协程中的职责与 Executor 在线程中的职责类似,但功能更多:

  • 内部持有一个 CoroutineContext(如 Job、调度器等),调度器底层可能用到线程池
  • 接受 CoroutineContext 参数,提供启动协程所需的上下文信息
// CoroutineScope = 协程世界的"Executor + 线程策略 + 生命周期管理"
val coroutineScope = CoroutineScope(Job() + Dispatchers.Default)

本质区别:Executor.execute(Runnable) 把代码装进一个 Runnable 对象;而 CoroutineScope.launch { } 把代码装进一个函数类型的对象suspend CoroutineScope.() -> Unit),编译器会为它生成状态机,支持挂起和恢复。

二、CoroutineScope.launch 源码:把代码装进函数对象

1launch 方法签名与实现
public fun CoroutineScope.launch(
    context: CoroutineContext = EmptyCoroutineContext,
    start: CoroutineStart = CoroutineStart.DEFAULT,
    block: suspend CoroutineScope.() -> Unit   //  函数类型,不是 Runnable
): Job {
    val newContext = newCoroutineContext(context)
    val coroutine = if (start.isLazy)
        LazyStandaloneCoroutine(newContext, block) else
        StandaloneCoroutine(newContext, active = true)
    coroutine.start(start, coroutine, block)
    return coroutine
}
block: suspend () -> Unit
->
newCoroutineContext
->
StandaloneCoroutine
->
coroutine.start()
对比维度Executor.execute(Runnable)CoroutineScope.launch { }
包装形式Runnable 对象(无返回值、不可挂起)suspend 函数类型(可挂起、可恢复)
返回值voidJob(可控:cancel、join)
线程切换固定在某线程池执行由 Dispatcher 灵活调度,可随时切换
生命周期通过 Job 树实现结构化并发

三、ContinuationInterceptor:协程的"切线程"核心接口

1调度器继承链

ContinuationInterceptor 就是协程世界中负责"切线程"的那个核心接口。你看到的 Dispatchers.DefaultDispatchers.Main 等,都是它的不同实现。

  • ContinuationInterceptor
    • CoroutineDispatcher
      • DefaultScheduler
      • MainCoroutineDispatcher
      • Unconfined
      • DefaultIoScheduler
      • ExecutorCoroutineDispatcher

Dispatchers 中的四个参数都是继承自 CoroutineDispatcher 或其子类,而 CoroutineDispatcher 继承自 ContinuationInterceptor

四、四种 Dispatchers 的源码定义

1Dispatchers 源码全貌
public actual object Dispatchers {
    @JvmStatic
    public actual val Default: CoroutineDispatcher = DefaultScheduler

    @JvmStatic
    public actual val Main: MainCoroutineDispatcher get() = MainDispatcherLoader.dispatcher

    @JvmStatic
    public actual val Unconfined: CoroutineDispatcher = kotlinx.coroutines.Unconfined

    @JvmStatic
    public val IO: CoroutineDispatcher get() = DefaultIoScheduler

    @DelicateCoroutinesApi
    public fun shutdown() {
        DefaultExecutor.shutdown()
        // Also shuts down Dispatchers.IO
        DefaultScheduler.shutdown()
    }
}
2四种调度器职责速查
调度器底层实现适用场景
Dispatchers.DefaultDefaultSchedulerCPU 密集型任务(排序、加密、解析)
Dispatchers.IODefaultIoSchedulerI/O 密集型任务(网络、文件、数据库)
Dispatchers.MainMainDispatcherLoader.dispatcherUI 相关任务(更新界面、操作 View)
Dispatchers.Unconfinedkotlinx.coroutines.Unconfined不关心线程的轻量操作(测试、底层库)

更多细节:四种调度器的线程池配置、阿姆达尔定律与弹性线程池设计原理,参见 四种调度器设计原理深度解析

五、自定义线程池:newFixedThreadPoolContext

1创建自定义调度器

一般情况下以上三种调度器就足够了。但如果特殊情况需要自己创建线程池,kotlinx-coroutines 提供了两个函数:

// 创建一个固定大小的线程池调度器
val context = newFixedThreadPoolContext(nThreads = 10, name = "MyThreadPool")

该方法返回的是 ExecutorCoroutineDispatcher 对象——它是 CoroutineDispatcher 的子类,并且实现了 Closeable 接口。在不需要该线程池时,记得调用 close() 关闭线程池,避免资源泄漏。

2单线程调度器:newSingleThreadContext

另一个函数是单线程上下文的便捷封装:

@ExperimentalCoroutinesApi
@DelicateCoroutinesApi
public fun newSingleThreadContext(name: String): CloseableCoroutineDispatcher =
    newFixedThreadPoolContext(1, name)

它本质上就是 newFixedThreadPoolContext(1, name)——创建一个只有 1 个线程的调度器,适合需要串行执行的场景。

注意:这两个 API 都标记了 @DelicateCoroutinesApi,意味着它们是精细 API,有特殊的使用注意事项(如必须手动关闭)。一般情况下优先使用内置的 Dispatchers。

3ExecutorCoroutineDispatcher 继承关系
  • ContinuationInterceptor
    • CoroutineDispatcher
      • ExecutorCoroutineDispatcher
        • newFixedThreadPoolContext() 返回值
        • newSingleThreadContext() 返回值

它还实现了 Closeable 接口,所以可以用 use { } 或手动 close() 来安全释放线程池资源。

六、核心思想总结

协程调度器与线程池核心要点

一句话总结

CoroutineScope 是协程世界的 Executor——它把代码从 Runnable 升级成了可挂起可恢复的 suspend 函数类型。Dispatchers 就是 ContinuationInterceptor 的不同实现,决定了协程跑在哪个线程上。内置四种够用,不够就用 newFixedThreadPoolContext 创建自己的线程池调度器。