Kotlin协程基础
开发环境
- IntelliJ IDEA 2021.2.2 (Community Edition)
- Kotlin: 212-1.5.10-release-IJ5284.40
我们已经通过第一个例子学会了启动协程,这里介绍一些协程的基础知识。
阻塞与非阻塞
runBlocking
delay是非阻塞的,Thread.sleep是阻塞的。显式使用 runBlocking 协程构建器来阻塞。
import kotlinx.coroutines.*
fun main() {
GlobalScope.launch { // 在后台启动一个新的协程并继续
delay(200)
"rustfisher.com".forEach {
print(it)
delay(280)
}
}
println("主线程中的代码会立即执行")
runBlocking { // 这个表达式阻塞了主线程
delay(3000L) //阻塞主线程防止过快退出
}
println("\n示例结束")
}
可以看到,runBlocking里使用了delay来延迟。用了runBlocking的主线程会一直阻塞直到runBlocking内部的协程执行完毕。
也就是runBlocking{ delay }实现了阻塞的效果。
我们也可以用runBlocking来包装主函数。
import kotlinx.coroutines.*
fun main() = runBlocking {
delay(100) // 在这里可以用delay了
GlobalScope.launch {
delay(100)
println("Fisher")
}
print("Rust ")
delay(3000)
}
runBlocking<Unit>中的<Unit>目前可以省略。
runBlocking也可用在测试中
// 引入junit
dependencies {
implementation("junit:junit:4.13.1")
}
单元测试
使用@Test设置测试
import org.junit.Test
import kotlinx.coroutines.*
class C3Test {
@Test
fun test1() = runBlocking {
println("[rustfisher] junit测试开始 ${System.currentTimeMillis()}")
delay(1234)
println("[rustfisher] junit测试结束 ${System.currentTimeMillis()}")
}
}
运行结果
[rustfisher] junit测试开始 1632401800686
[rustfisher] junit测试结束 1632401801928
IDEA可能会提示no tasks available。需要把测试选项改为IDEA,如下图。

等待
有时候需要等待协程执行完毕。可以用join()方法。这个方法会暂停当前的协程,直到执行完毕。需要用main() = runBlocking。
import kotlinx.coroutines.*
fun main() = runBlocking {
println("[rustfisher]测试等待")
val job1 = GlobalScope.launch {
println("job1 start")
delay(300)
println("job1 done")
}
val job2 = GlobalScope.launch {
println("job2 start")
delay(800)
println("job2 done")
}
job2.join()
job1.join() // 等待
println("测试结束")
}
运行log
[rustfisher]测试等待
job1 start
job2 start
job1 done
job2 done
测试结束
结构化的并发
用GlobalScope.launch时,会创建一个顶层协程。之前的例子我们也知道,它不使用主线程。新创的协程虽然轻量,但仍会消耗一些内存资源。如果忘记保持对新启动的协程的引用,它还会继续运行。
我们可以在代码中使用结构化并发。
示例中,我们使用runBlocking协程构建器将main函数转换为协程。在里面(作用域)启动的协程不需显式使用join。
观察下面的例子:
import kotlinx.coroutines.*
fun main() = runBlocking<Unit> {
println("主线程id ${Thread.currentThread().id}")
launch { // 在 runBlocking 作用域中启动一个新协程1
println("协程1所在线程id ${Thread.currentThread().id}")
delay(300)
println("协程1执行完毕")
}
launch { // 在 runBlocking 作用域中启动一个新协程2
println("协程2所在线程id ${Thread.currentThread().id}")
delay(500)
println("协程2执行完毕")
}
println("主线程执行完毕")
}
运行log
主线程id 1
主线程执行完毕
协程1所在线程id 1
协程2所在线程id 1
协程1执行完毕
协程2执行完毕
可以看到,不用像之前那样调用Thread.sleep或者delay让主线程等待一段时间,防止虚拟机退出。
程序会等待它所有的协程执行完毕,然后真正退出。
作用域构建器
使用 coroutineScope 构建器声明自己的作用域。它会创建一个协程作用域,并且会等待所有已启动子协程执行完毕。
runBlocking 与 coroutineScope 看起来类似,因为它们都会等待其协程体以及所有子协程结束。主要区别在于:
runBlocking方法会阻塞当前线程来等待,是常规函数coroutineScope只是挂起,会释放底层线程用于其他用途,是挂起函数
下面这个示例展示了作用域构建器的特点。main是一个作用域。
import kotlinx.coroutines.*
fun main() = runBlocking { // this: CoroutineScope
launch {
delay(200L)
println("协程1 t${Thread.currentThread().id}")
}
coroutineScope { // 创建一个协程作用域
launch {
delay(500L)
println("内部协程2-1 t${Thread.currentThread().id}")
}
delay(100L)
println("协程2 t${Thread.currentThread().id}")
}
println("主任务完毕")
}
运行log
协程2 t1
协程1 t1
内部协程2-1t1
主任务完毕
提取函数重构
将launch { …… } 内部的代码块提取到独立的函数中。提取出来的函数需要 suspend 修饰符,它是挂起函数。
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
fun main() = runBlocking<Unit> {
launch { r1() }
println("DONE")
}
// 挂起函数
suspend fun r1() {
delay(300)
println("[rustfisher] 提取出来的函数")
}
log
DONE
[rustfisher] 提取出来的函数
协程是轻量的
我们前面也试过,创建非常多的协程,程序运行OK。
下面的代码可以输出很多的点
import kotlinx.coroutines.*
fun main() = runBlocking {
for (t in 1..10000) {
launch {
delay(t * 500L)
print(".")
}
}
}
全局协程像守护线程
我们在线程介绍中知道,如果进程中只剩下了守护线程,那么虚拟机会退出。
前文那个打印rustfisher.com的例子,其实也能看到,字符没打印完程序就结束了。
在 GlobalScope 中启动的活动协程并不会使进程保活。它们就像守护线程。
再举一个例子
import kotlinx.coroutines.*
fun main() = runBlocking {
GlobalScope.launch {
for (i in 1..1000000) {
delay(200)
println("协程执行: $i")
}
}
delay(1000)
println("Bye~")
}
log
协程执行: 1
协程执行: 2
协程执行: 3
协程执行: 4
Bye~
最后我们来看一下全文的思路

参考
Kotlin协程基础的更多相关文章
- Kotlin协程解析系列(上):协程调度与挂起
vivo 互联网客户端团队- Ruan Wen 本文是Kotlin协程解析系列文章的开篇,主要介绍Kotlin协程的创建.协程调度与协程挂起相关的内容 一.协程引入 Kotlin 中引入 Corout ...
- Kotlin协程第一个示例剖析及Kotlin线程使用技巧
Kotlin协程第一个示例剖析: 上一次https://www.cnblogs.com/webor2006/p/11712521.html已经对Kotlin中的协程有了理论化的了解了,这次则用代码来直 ...
- Retrofit使用Kotlin协程发送请求
Retrofit2.6开始增加了对Kotlin协程的支持,可以通过suspend函数进行异步调用.本文简单介绍一下Retrofit中协程的使用 导入依赖 app的build文件中加入: impleme ...
- Android Kotlin协程入门
Android官方推荐使用协程来处理异步问题.以下是协程的特点: 轻量:单个线程上可运行多个协程.协程支持挂起,不会使正在运行协程的线程阻塞.挂起比阻塞节省内存,且支持多个并行操作. 内存泄漏更少:使 ...
- Kotlin 协程一 —— 全面了解 Kotlin 协程
一.协程的一些前置知识 1.1 进程和线程 1.1.1基本定义 1.1.2为什么要有线程 1.1.3 进程与线程的区别 1.2 协作式与抢占式 1.2.1 协作式 1.2.2 抢占式 1.3 协程 二 ...
- rxjava回调地狱-kotlin协程来帮忙
本文探讨的是在tomcat服务端接口编程中, 异步servlet场景下( 参考我另外一个文章),用rxjava来改造接口为全流程异步方式 好处不用说 tomcat的worker线程利用率大幅提高,接口 ...
- pyhon——进程线程、与协程基础概述
一直以来写博客都是实用主义者,只写用法,没信心写原理,但是每一次写作业的过程都有一种掘地三尺的感觉,终于,写博客困难症重症患者经历了漫长的思想斗争,还是决定把从网上淘到的各种杂货和自己的总结放在一起, ...
- Kotlin协程通信机制: Channel
Coroutines Channels Java中的多线程通信, 总会涉及到共享状态(shared mutable state)的读写, 有同步, 死锁等问题要处理. 协程中的Channel用于协程间 ...
- Kotlin协程作用域与Job详解
Job详解: 在上一次https://www.cnblogs.com/webor2006/p/11725866.html中抛出了一个问题: 所以咱们将delay去掉,需要改造一下,先把主线程的dela ...
随机推荐
- Python小白的数学建模课-18.最小生成树问题
最小生成树(MST)是图论中的基本问题,具有广泛的实际应用,在数学建模中也经常出现. 路线设计.道路规划.官网布局.公交路线.网络设计,都可以转化为最小生成树问题,如要求总线路长度最短.材料最少.成本 ...
- SpringCloud War 包部署导致服务未正常注册到 Nacos 问题
转载地址:https://blog.csdn.net/qq_28379809/article/details/103773149
- Unity遮罩之Mask、RectMask2D与Sprite Mask适用场景分析
遮罩,顾名思义是一种可以掩盖其它元素的控件.常用于修改其它元素的外观,或限制元素的形状.比如ScrollView或者圆头像效果都有用到遮罩功能.本系列文章希望通过阅读UGUI源码的方式,来探究遮罩的实 ...
- Python3 * 和 ** 运算符
1.算数运算 * 代表乘法 ** 代表乘方 1>>> 2 * 52103>>> 2 ** 5432 2.函数形参 *args 和 **kwargs 主要用于函数定 ...
- Kafka 与 RabbitMQ 如何选择使用哪个?
目录 前言 如何选择? 开发语言 延迟队列 消息顺序性 优先级队列 消息留存 消息过滤 可伸缩行 小结 推荐阅读 前言 我们在工作中经常会用到异步消息,主要使用两种消息模式: 消息队列 发布/订阅 消 ...
- 数据结构--Dijkstra算法最清楚的讲解
迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径.它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止 ###基本思想 通过Dij ...
- WPF 饼状图,柱形图,折线图 (1 柱形图)
WPF三贱客绘制,柱形图应该是比较简单的一个了.效果如下: ItemSource数据结构可自己定义,我的如下列子,自定义的数据结构属性,要对应配置下DisplayMemberMsg 和DisplayM ...
- jsoup的Node类
一.简介 Node类直接继承Object,实现了Cloneable接口,它是一个抽象类,类声明:public abstract class Node extends Object implements ...
- mzy git学习,初识git(一)
GIT学习 git工作区.暂存区.本地库.远程库 工作区:实际上我们工作的地方,进行写代码或者文件的地方. 暂存区:我们执行了git add 操作之后,就会被提交到暂存区. 本地库:其实最后我们需要执 ...
- 回顾games101中的SSAA和MSAA
回顾games101中的AA(抗锯齿) 前言 善于进行课后总结,可以更加巩固自己的知识和具体细节 锯齿(走样)产生的原因 本质上,在光栅化阶段中,用有限离散的数据想表示连续的(类似三角形的某一边),就 ...