1. 阻塞与非阻塞
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("junit 测试开始 ${System.currentTimeMillis()}")
delay(1234)
println("junit 测试完结 ${System.currentTimeMillis()}")
}
}
运行后果
junit 测试开始 1632401800686
junit 测试完结 1632401801928
IDEA 可能会提醒no tasks available
。须要把测试选项改为 IDEA,如下图。
更改设置
2. 期待
有时候须要期待协程执行结束。能够用 join()
办法。这个办法会暂停以后的协程,直到执行结束。须要用main() = runBlocking
。
import kotlinx.coroutines.*
fun main() = runBlocking {println("测试期待")
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
测试期待
job1 start
job2 start
job1 done
job2 done
测试完结
3. 结构化的并发
用 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
让主线程期待一段时间,避免虚拟机退出。
程序会期待它所有的协程执行结束,而后真正退出。
4. 作用域构建器
应用 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
主工作结束
5. 提取函数重构
将 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("提取进去的函数")
}
log
DONE
提取进去的函数
6. 协程是轻量的
咱们后面也试过,创立十分多的协程,程序运行 OK。
上面的代码能够输入很多的点
import kotlinx.coroutines.*
fun main() = runBlocking {for (t in 1..10000) {
launch {delay(t * 500L)
print(".")
}
}
}
7. 全局协程像守护线程
如果过程中只剩下了守护线程,那么虚构机会退出。前文那个的例子,其实也能看到,字符没打印完程序就完结了。
在 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 强化实战
140 集 Kotlin 入门到精通全系列(我的项目开发实战)视频教程