Kotlin Coroutines 笔记 (一)

简介: Kotlin Coroutines 笔记 (一)

一. 协程



Kotlin 在1.1版本之后引入了协程的概念,目前它还是一个试验的API。


在操作系统中,我们知道进程和线程的概念以及区别。而协程相比于线程更加轻量级,协程又称微线程。


协程是一种用户态的轻量级线程,协程的调度完全由用户控制。协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈,直接操作栈则基本没有内核切换的开销,可以不加锁的访问全局变量,所以上下文的切换非常快。


Kotlin 的协程是无阻塞的异步编程方式。Kotlin 允许我们使用协程来代替复杂的线程阻塞操作,并且复用原本的线程资源。


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 秒。


image.png

十万个协程测试.jpeg

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")
}


image.png

十万个线程测试.jpeg


10w个线程的创建出现了OutOfMemoryError。


二. 协程常用的基本概念



2.1 CoroutineContext


协程上下文,它包含了一个默认的协程调度器。所有协程都必须在 CoroutineContext 中执行。


2.2 CoroutineDispatcher


协程调度器,它用来调度和处理任务,决定了相关协程应该在哪个或哪些线程中执行。Kotlin 的协程包含了多种协程调度器。


2.3 Continuation


按照字面意思是继续、持续的意思。协程的执行可能是分段执行的:先执行一段,挂起,再执行一段,再挂起......


Continuation 则表示每一段执行的代码,Continuation 是一个接口。


2.4 Job


任务执行的过程被封装成 Job,交给协程调度器处理。Job 是一种具有简单生命周期的可取消任务。Job 拥有三种状态:isActive、isCompleted、isCancelled。

wait children
    +-----+       start      +--------+   complete   +-------------+  finish  +-----------+
    | New | ---------------> | Active | -----------> | Completing  | -------> | Completed |
    +-----+                  +--------+              +-------------+          +-----------+
       |                         |                         |
       | cancel                  | cancel                  | cancel
       V                         V                         |
  +-----------+   finish   +------------+                  |
  | Cancelled | <--------- | Cancelling | <----------------+
  |(completed)|            +------------+
  +-----------+


2.5 Deferred


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)|            +------------+                                  +-----------+
 +-----------+


2.6 suspend 关键字


协程计算可以被挂起而无需阻塞线程。我们使用 suspend 关键字来修饰可以被挂起的函数。被标记为 suspend 的函数只能运行在协程或者其他 suspend 函数中。


suspend 可以修饰普通函数、扩展函数和 lambda 表达式。


三. 协程的多种使用方式



Kotlin 的协程支持多种异步模型:


image.png

Kotlin协程支持的异步模型.png


这些异步机制在 Kotlin 的协程中都有实现。


Kotlin 官方对协程提供的三种级别的能力支持, 分别是: 最底层的语言层, 中间层标准库(kotlin-stdlib), 以及最上层应用层(kotlinx.coroutines)。


3.1 协程的hello world版本


使用 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 的协程能够简化异步编程的代码,使用同步的方式实现异步。协程的概念和理论比较多,第一篇只是一个开始,只整理了其中一些基本概念。

相关文章
|
25天前
|
算法 Kotlin
Kotlin教程笔记(24) -尾递归优化
Kotlin教程笔记(24) -尾递归优化
43 7
Kotlin教程笔记(24) -尾递归优化
|
26天前
|
Java Kotlin
​ Kotlin教程笔记(15) - 方法重载与默认参数
​ Kotlin教程笔记(15) - 方法重载与默认参数
29 4
​ Kotlin教程笔记(15) - 方法重载与默认参数
|
23天前
|
Java 编译器 Kotlin
Kotlin入门笔记1 - 数据类型
Kotlin入门笔记1 - 数据类型
68 15
|
25天前
|
Kotlin
Kotlin教程笔记(20) - 枚举与密封类
Kotlin教程笔记(20) - 枚举与密封类
38 8
|
25天前
|
安全 Java 编译器
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
Kotlin教程笔记(27) -Kotlin 与 Java 共存(二)
|
25天前
|
Java 开发工具 Android开发
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
Kotlin教程笔记(26) -Kotlin 与 Java 共存(一)
|
25天前
|
安全 Kotlin
Kotlin教程笔记(23) -作用域函数
Kotlin教程笔记(23) -作用域函数
74 6
|
25天前
|
Kotlin
Kotlin教程笔记(21) -高阶函数与函数引用
Kotlin教程笔记(21) -高阶函数与函数引用
33 6
|
25天前
|
缓存 Kotlin Python
Kotlin教程笔记(25) -函数式编程
Kotlin教程笔记(25) -函数式编程
|
24天前
|
设计模式 Java Kotlin
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
Kotlin教程笔记(56) - 改良设计模式 - 装饰者模式
37 2