一、概念
- 协程:本质就是线程框架。一套基于 Thread(线程) 封装的 工具API(就像 Java 的 Executor 、Android 的 AsyncTask Handler 以及 RxJava)。
- 轻量:线程由系统调度,切换或阻塞有开销。协程是能人为的指定任务的执行顺序和线程(指定在哪类线程上执行,线程调度依旧是系统)。
- 用户态:协程没有个数限制由程序控制(开发者),而线程是内核态需要依赖系统(CPU核心数)。
- 挂起函数:挂起函数挂起的是协程,让协程和线程分离让出线程,等挂起函数执行完返回到协程中,恢复在之前的线程上执行后续代码。本质同样是切线程做其他事情,只是做完了可以回来继续做之前的事。
- 非阻塞式:单线程是阻塞式的,当前任务没做完就不会执行后面的代码,多线程是切到别的线程执行就不会阻塞之前的线程。协程借助 Kotlin 语言的简洁优势以及“挂起恢复”“的特性消除了 Java 多线程那样的回调嵌套,即原先串行写的代码现在并行来写(让串行嵌套的异步代码像同步那样并行编写)。
- 结构化并发:就是在处理整体与局部、局部与局部之间的关系。父协程提供了一个作用域,子协程都在该作用域中被创建,由此构成了结构化并发的关系:
- 子协程会继承父协程的上下文,并可被改写。
- 父协程会等待所有子协程结束。
- 当父协程被取消时,所有子协程也会被取消。
- 通过Job双向传播取消和异常,当某个子协程发生异常时会连带取消整个协程。
二、协程 Coroutine
所有协程都是 AbstractContinuation 抽象类的子类,而它继承了 Job、CoroutineScope、Continuation。利用多态隐藏复杂实现细节只暴露出必要的API:在创建协程作用域对象的时候返回的是 CoroutineScope 类型指定上下文,在调用协程构建器时返回的是 Job 控制生命周期,在调用挂起函数传递的是 Continuation 用来挂起恢复。
当创建一个协程对象的时候返回的是 Job 而不是 AbstractContinuation 具体的实现类,因为我们目的是按顺序执行逻辑任务以及控制协程的生死以及获取值,(利用多态)隐藏复杂实现细节只暴露出必要的API,而 CoroutineScope 和 Continuation 的作用是针对协程内部功能的实现。
public abstract class AbstractCoroutine<in T>(
parentContext: CoroutineContext,
initParentJob: Boolean,
active: Boolean
) : JobSupport(active), Job, Continuation<T>, CoroutineScope { }
【子类】 | 【说明】 |
BlockingCoroutine | runBlocking() 启动的协程对象 |
LazyStandaloneCoroutine | launch() 启动的延迟执行的协程 |
StandaloneCoroutine | launch() 启动的立即执行的协程 |
LazyDeferredCoroutine | async() 启动的延迟执行的协程 |
DeferredCoroutine | async() 启动的立即执行的协程 |
DispatchedCoroutine | withContext() 启动的协程 |
FlowCoroutine | flow() 相关的协程 |
三、协程的三层包装
三层包装都实现了 Contineation 接口,通过装饰模式(后者持有前者)将各层组合在一起,每层实现了不同的功能,都是协程但不完整。
1层 | CoroutineBuilder 协程构建器 | launch() → Job → StandaloneCoroutine → AbstractCoroutine → Coroutine async() → Deferred → DeferredCoroutine → AbstractCoroutine → Coroutine 返回的Job和Deferred封装了协程的状态并提供了取消协程的接口。 |
2层 | Suspend Funtion 挂起函数 | SuspendLambda → ContinuationImpl → BaseContinuationImpl → Coroutine 封装业务代码和执行顺序(invokeSuspend()函数),持有第一层包装(成员属性completion)。 |
3层 | CoroutineDispatcher 协程调度器 | DispatchedContinuation → Coroutine 封装线程调度逻辑(成员属性dispatcher),持有第二层包装(成员属性continuation)。 |
一些函数
currentCoroutineContext( ) | public suspend inline fun currentCoroutineContext(): CoroutineContext = coroutineContext 获取当前协程的上下文。(在子协程中通过coroutineContext获取到的是父协程的上下文) |