这段话来自kolin中文网:本质上,协程是轻量级的线程。 它们在某些 CoroutineScope 上下文中与 launch 协程构建器 一起启动。
1.GlobalScope启动全局新协程
这里我们在 GlobalScope 中启动了一个新的协程,这意味着新协程的生命周期只受整个应用程序的生命周期限制。
fun main1() {
GlobalScope.launch {
// 在后台启动一个新的协程并继续
delay(1000L) // 非阻塞的等待 1 秒钟(默认时间单位是毫秒)
Log.i("协程---", "G.launch.delay(1000L) " + System.currentTimeMillis())//-----1
}
Log.i("协程---Start,", "协程" + System.currentTimeMillis())//主线程的代码会立即执行-----0秒开始执行
Thread.sleep(2000L) // 阻塞主线程 2 秒钟来保证 JVM 存活
Log.i("协程---", "Thread.sleep(2000L)" + System.currentTimeMillis())//-----2
runBlocking {
// 但是这个表达式阻塞了主线程
delay(2000L) // ……我们延迟 2 秒来保证 JVM 的存活
Log.i("协程---", "runBlocking(2000L) " + System.currentTimeMillis())//-----4
}
}
注意这里 Thread.sleep 线程相关,会阻塞主线程。一下runBlocking中多延迟了两秒,而delay没有阻塞主线程
输出结果:
2.强制阻塞(runBlocking)
第一个示例在同一段代码中混用了 非阻塞的 delay(……)
与 阻塞的 Thread.sleep(……)
。 这容易让我们记混哪个是阻塞的、哪个是非阻塞的。 让我们显式使用 runBlocking 协程构建器来阻塞:
// 桥接阻塞与非阻塞,里面的delay都是看起来是顺序执行,阻塞了彼此
fun main2() = runBlocking<Unit> {
GlobalScope.launch {
// 在后台启动一个新的协程并继续
delay(1000L)
Log.i("协程---", "G.launch.delay(1000L) " + System.currentTimeMillis())//-----
}
Log.i("协程---Start", "协程" + System.currentTimeMillis()) // 主协程在这里会立即执行
delay(2000L) // 延迟 2 秒来保证 JVM 存活
Log.i("协程---", "delay(2000)" + System.currentTimeMillis())//-----
delay(2000L) // 延迟 2 秒来保证 JVM 存活
Log.i("协程---", "delay(2000)" + System.currentTimeMillis())//-----
}
输出:
3.join 等待作业
控制异步程序执行的简单明了的一种方式:作业执行完毕后 才走 job。join之后的代码
fun main3() = runBlocking {
val job = GlobalScope.launch {
// 启动一个新协程并保持对这个作业的引用
delay(1000L)
Log.i("协程---", "G.launch.delay(1000L) " + System.currentTimeMillis())//-----1
}
Log.i("协程---", "start " + System.currentTimeMillis())//-----0
job.join() // 等待直到子协程执行结束
Log.i("协程---", "job.join(2000L)join后" + System.currentTimeMillis())//-----1
}
输出:
4 结构化并发,记住与3相互比较一下 更简洁 更常用
这里注意,lauch中耗时操作 执行后 runBlocking 才会结束 而global.lauch不属于当前block作用域 也就是说,外面调用什么东西的话 完全可以等到当前耗时操作获取到结果后获取,这样就不用写join了 runBlocking所包裹到代码中,才可以直接有lauch{},但是这时候也就限制了只有lauch挂起执行完毕,此blocking块才能结束,才能继续走block以外的代码
fun main4() = runBlocking {
val job = launch {
// 启动一个新协程并保持对这个作业的引用
delay(1000L)
Log.i("协程---","G.launch.delay(1000L) "+System.currentTimeMillis())//-----2
// main4lauch()
}
Log.i("协程---", "Start " + System.currentTimeMillis())//-----2
}
this.main4()
Log.i("协程---这一行主线程,最后执行", "协程" + System.currentTimeMillis())//-----1
输出:
5 重要:协程作用域构建器:coroutineScope
//作用域构建器 /* 协程作用域 执行完毕后外面线程才会执行 * 不加协程作用域,外面runblock会先执行 * 如果有内嵌coroutineScope,则runblock中初层代码在内嵌scope之后执行,否则直接最先就执行了 * * 1。有coroutineScope,: 先(lauch或coroutinescope.lauch, -> 初层代码) * * 2。无coroutineScope : 先 初层代码 -》 lauch(或同时) * * */
fun main5() = runBlocking {
// this: CoroutineScope
launch {
Log.i("协程---( lauch", "执行lunch" + System.currentTimeMillis())// 这一行会在内嵌 launch 之前输------0
delay(20000L)
Log.i("协程---( lauch", "执行完了lunch" + System.currentTimeMillis())// 这一行会在内嵌 launch 之前输------2
}
coroutineScope{
// 创建一个协程作用域
launch {
Log.i("协程---( coroutinelauch", "执行coroutinelauch" + System.currentTimeMillis())// 这一行会在内嵌 launch 之前输------0
delay(5000L)
Log.i("协程---( Coroutine lauch", "coroutinelauch" + System.currentTimeMillis())//----5
}
delay(1000L)
Log.i("协程---( coroutineScope", "launch前" + System.currentTimeMillis())// 这一行会在内嵌 launch 之前输出-----1
}
Log.i("协程---( Tasklaunch", "launch后" + System.currentTimeMillis()) // 这一行在 内嵌 launch 执行完毕后才输出----5
}
输出:
6.守护线程的一种方式
重复五次 repeat(5)
fun main6() = runBlocking {
//sampleStart
GlobalScope.launch {
repeat(5) { i ->
delay(500L)
Log.i("协程---( im slepping $i", "500l后" + System.currentTimeMillis()) //
}
}
Log.i("协程---( im slepping", "延迟1300前" + System.currentTimeMillis()) //
delay(1300L) // just quit after delay
Log.i("协程---( im slepping", "延迟1300l" + System.currentTimeMillis()) //
//sampleEnd
}
输出:
7 suspend 关键字
运行在lauch中的挂起函数要添加suspend关键字如:
fun main4() = runBlocking {
val job = launch {
// 启动一个新协程并保持对这个作业的引用
main4lauch()
}
Log.i("协程---", "Start " + System.currentTimeMillis())//----
}
suspend fun main4lauch() {
delay(1000L)
Log.i("协程---mai4(1000L", "函数重构" + System.currentTimeMillis())//----
}
lauch本身就是suspend的
输出: