协程是一种协作式的计算机程序并发调度的实现,程序可以主动挂起或者恢复执行,本质上,协程是轻量级的线程。
1、协程是协作式的,线程是抢占式的。协程是由程序来控制什么时候进行切换的,而线程是有操作系统来决定线程之间的切换的。
2、一个线程可以包含多个协程。但是有一点必须明确的是,一个线程的多个协程的运行是串行的。
3、和多线程比,协程没有线程切换的开销,执行效率更高,性能更具优势。因而协程适合io密集型的程序,多线程适合计算密集型的程序(适用于多核cpu的情况)。当你的程序大部分是文件读写操作或者网络请求操作的时候,这时你应该首选协程。因为这些操作大部分不是利用cpu进行计算而是等待数据的读写。
4、协程,能简化异步执行的代码,书写更优雅,避免回调地狱。
1、suspend :挂起函数
suspend fun requestToken(): Token { ... } // 挂起函数 suspend fun createPost(token: Token, item: Item): Post { ... } fun processPost(post: Post) { GlobalScope.launch { // 创建一个新协程 val token = requestToken() val post = createPost(token, item) processPost(post) // 需要异常处理,直接加上 try/catch 语句即可 } }requestToken和createPost函数前面有suspend修饰符标记,这表示两个函数都是挂起函数。挂起函数能够以与普通函数相同的方式获取参数和返回值,但是调用函数可能挂起协程(如果相关调用的结果已经可用,库可以决定继续进行而不挂起),挂起函数挂起协程时,不会阻塞协程所在的线程。挂起函数执行完成后会恢复协程,后面的代码才会继续执行。需要注意的是,挂起函数只能在协程中或其他挂起函数中调用。
2、GlobalScope.launch:协程的创建
由上可知,我们可以通过GlobalScope.launch创建一个协程
(创建协程有三种方式: launch、async、runBlocking)
GlobalScope.launch的源码如下:
public fun CoroutineScope.launch( context: CoroutineContext = EmptyCoroutineContext, start: CoroutineStart = CoroutineStart.DEFAULT, block: suspend CoroutineScope.() -> Unit )3、CoroutineContext:协程上下文
CoroutineContext,协程上下文,是一些元素的集合,主要包括 Job 和 CoroutineDispatcher 元素,可以代表一个协程的场景。
(1) CoroutineDispatcher,协程调度器,决定协程所在的线程或线程池。主要有以下几种调度器:
Dispatchers.Main:主线程,用来处理UI交互和一些轻量级任务Dispatchers.IO:非主线程,专为磁盘和网络IO进行了优化。适用于数据库、文件读写,网络处理等场景Dispatchers.Default:默认调度器,非主线程,专为CPU密集型任务进行了优化,适用于数组排序,JSON解析等场景Dispatchers.Unconfined:不限定调度器,适用于执行不消耗 CPU 时间的任务,以及不更新如UI的协程(2) Job & Deferred
Job 可以取消,且有简单生命周期。有几种生命周期状态:new,active,Completing,Cancelling, Cancelled,Completed 。
Job 完成时是没有返回值的,而Deferred是有返回值的,Deferred是Job的子类。
4、runBlocking
创建一个新的协程同时阻塞当前线程,直到协程结束。
5、withContext
withContext{}不会创建新的协程,在指定协程上运行挂起代码块,并挂起该协程直至代码块运行完成。
6、async
后台创建一个新协程,相比于launch,它有返回值,返回的是 Deferred 类型
获取返回值的方法是通过await()函数,这也是个挂起函数,调用时会挂起当前协程直到 async 中代码执行完并返回某个值。