Kotlin 在1.1版本以后引入了协程的概念,目前它仍是一个试验的API。编程
在操做系统中,咱们知道进程和线程的概念以及区别。而协程相比于线程更加轻量级,协程又称微线程。bash
协程是一种用户态的轻量级线程,协程的调度彻底由用户控制。协程拥有本身的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其余地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操做栈则基本没有内核切换的开销,能够不加锁的访问全局变量,因此上下文的切换很是快。异步
Kotlin 的协程是无阻塞的异步编程方式。Kotlin 容许咱们使用协程来代替复杂的线程阻塞操做,而且复用本来的线程资源。async
Kotlin 的协程是依靠编译器实现的, 并不须要操做系统和硬件的支持。编译器为了让开发者编写代码更简单方便, 提供了一些关键字(例如suspend
), 并在内部自动生成了一些支持型的代码。异步编程
先举两个例子来讲明协程的轻量级,分别建立10w个协程和10w个线程进行测试。函数
import kotlinx.coroutines.experimental.CommonPool
import kotlinx.coroutines.experimental.delay
import kotlinx.coroutines.experimental.launch
import kotlinx.coroutines.experimental.runBlocking
/** * Created by tony on 2018/7/18. */
fun main(args: Array<String>) {
val start = System.currentTimeMillis()
runBlocking {
val jobs = List(100000) {
// 建立新的coroutine
launch(CommonPool) {
// 挂起当前上下文而非阻塞1000ms
delay(1000)
println("thread name="+Thread.currentThread().name)
}
}
jobs.forEach {
it.join()
}
}
val spend = (System.currentTimeMillis()-start)/1000
println("Coroutines: spend= $spend s")
}
复制代码
10w个协程的建立在本机大约花费 1 秒,通过测试100w个协程的建立大约花费11 秒。测试
import kotlin.concurrent.thread
/** * Created by tony on 2018/7/18. */
fun main(args: Array<String>) {
val start = System.currentTimeMillis()
val threads = List(100000) {
// 建立新的线程
thread {
Thread.sleep(1000)
println(Thread.currentThread().name)
}
}
threads.forEach { it.join() }
val spend = (System.currentTimeMillis()-start)/1000
println("Threads: spend= $spend s")
}
复制代码
10w个线程的建立出现了OutOfMemoryError。ui
协程上下文,它包含了一个默认的协程调度器。全部协程都必须在 CoroutineContext 中执行。spa
协程调度器,它用来调度和处理任务,决定了相关协程应该在哪一个或哪些线程中执行。Kotlin 的协程包含了多种协程调度器。操作系统
按照字面意思是继续、持续的意思。协程的执行多是分段执行的:先执行一段,挂起,再执行一段,再挂起......
Continuation 则表示每一段执行的代码,Continuation 是一个接口。
任务执行的过程被封装成 Job,交给协程调度器处理。Job 是一种具备简单生命周期的可取消任务。Job 拥有三种状态:isActive、isCompleted、isCancelled。
wait children
+-----+ start +--------+ complete +-------------+ finish +-----------+
| New | ---------------> | Active | -----------> | Completing | -------> | Completed |
+-----+ +--------+ +-------------+ +-----------+
| | |
| cancel | cancel | cancel
V V |
+-----------+ finish +------------+ |
| Cancelled | <--------- | Cancelling | <----------------+
|(completed)| +------------+
+-----------+
复制代码
Deferred 是 Job 的子类。Job 完成时是没有返回值的,Deferred 能够为任务完成时提供返回值,而且Deferred 新增了一个状态 isCompletedExceptionally。
wait children
+-----+ start +--------+ complete +-------------+ finish +-----------+
| New | ---------------> | Active | ----------> | Completing | ---+-> | Resolved |
+-----+ +--------+ +-------------+ | |(completed)|
| | | | +-----------+
| cancel | cancel | cancel |
V V | | +-----------+
+-----------+ finish +------------+ | +-> | Failed |
| Cancelled | <--------- | Cancelling | <---------------+ |(completed)|
|(completed)| +------------+ +-----------+
+-----------+
复制代码
协程计算能够被挂起而无需阻塞线程。咱们使用 suspend 关键字来修饰能够被挂起的函数。被标记为 suspend 的函数只能运行在协程或者其余 suspend 函数中。
suspend 能够修饰普通函数、扩展函数和 lambda 表达式。
Kotlin 的协程支持多种异步模型:
这些异步机制在 Kotlin 的协程中都有实现。
Kotlin 官方对协程提供的三种级别的能力支持, 分别是: 最底层的语言层, 中间层标准库(kotlin-stdlib), 以及最上层应用层(kotlinx.coroutines)。
使用 launch 和 async 都能启动一个新的协程。
val job = launch {
delay(1000)
println("Hello World!")
}
Thread.sleep(2000)
复制代码
或者
val deferred = async {
delay(1000)
println("Hello World!")
}
Thread.sleep(2000)
复制代码
它们分别会返回一个 Job 对象和一个 Deferred 对象。
下面使用 runBlocking 来建立协程。
fun main(args: Array<String>) = runBlocking<Unit> {
launch {
delay(1000)
println("Hello World!")
}
delay(2000)
}
复制代码
runBlocking 建立的协程直接运行在当前线程上,同时阻塞当前线程直到结束。
launch 和 async 在建立时可使用不一样的CoroutineDispatcher,例如:CommonPool。
在 runBlocking 内还能够建立其余协程,例如launch。反之则不行。
Kotlin 的协程可以简化异步编程的代码,使用同步的方式实现异步。协程的概念和理论比较多,第一篇只是一个开始,只整理了其中一些基本概念。
Java与Android技术栈:每周更新推送原创技术文章,欢迎扫描下方的公众号二维码并关注,期待与您的共同成长和进步。