参考链接
示例来自bilibili Kotlin语言深入解析 张龙老师的视频
1 Job的join方法
import kotlinx.coroutines.*
/**
* Job的join方法
* 它会挂起协程 直到Job完成
* join能够实现多个协程合作 即 一个协程等待另一个协程完成后执行
*
* Job是一个后台的Job。概念上讲,job是一个可以取消的 有生命周期的东西,job完成后它的生命周期就结束了
* A background job. Conceptually, a job is a cancellable thing with a life-cycle that culminates in its completion.
*/
fun main() = runBlocking {
val myJob:Job = GlobalScope.launch {//GlobalScope.launch开启协程 协程不阻塞当前线程 并计时1s
delay(1000)
println("Kotlin Coroutines")
}
println("hello")// 主线程继续执行 输出hello
myJob.join() // myJob.join() 挂起协程(GlobalScope.launch创建的协程) 直到myJob执行完毕
println("world")
}
/**
* join 方法详解
* join方法会挂起当前协程直到job完成。当job出于任何原因而完成该调用就可以正常恢复(没有异常的情况下) 并且Job依赖的协程仍然是active的
* Suspends the coroutine until this job is complete. This invocation resumes normally (without exception)
* when the job is complete for any reason and the [Job] of the invoking coroutine is still [active][isActive].
* 如果Job仍然处于new state的状态 该方法也会启动对应的协程
* This function also [starts][Job.start] the corresponding coroutine if the [Job] was still in _new_ state.
*
* 只有这个job的所有子job完成 当前job才能完成
* Note that the job becomes complete only when all its children are complete.
*
* 这个挂起方法是可以取消的并且一直在检查 调用协程Job是否被取消
* This suspending function is cancellable and **always** checks for a cancellation of the invoking coroutine's Job.
* 如果在调用此挂起函数时或在挂起时调用协程的 [Job] 被取消或完成,则此函数将抛出 [CancellationException]。
* If the [Job] of the invoking coroutine is cancelled or completed when this
* suspending function is invoked or while it is suspended, this function
* throws [CancellationException].
*
* 特别是,这意味着父协程在使用 `launch(coroutineContext) { ... }` 构建器启动的子协程上调用 `join`,如果子进程崩溃,则会抛出 [CancellationException],
* 除非在上下文中安装了非标准的 [CoroutineExceptionHandler]。
* In particular, it means that a parent coroutine invoking `join` on a child coroutine that was started using
* `launch(coroutineContext) { ... }` builder throws [CancellationException] if the child
* had crashed, unless a non-standard [CoroutineExceptionHandler] is installed in the context.
*
* 该函数可以在带有 [onJoin] 子句的 [select] 调用中使用。 使用 [isCompleted] 无需等待即可检查此作业是否已完成。
* This function can be used in [select] invocation with [onJoin] clause.
* Use [isCompleted] to check for a completion of this job without waiting.
*
* [cancelAndJoin] 函数结合了 [cancel] 和 `join` 的调用。
* There is [cancelAndJoin] function that combines an invocation of [cancel] and `join`.
*/
/**
* 输出
* hello
* Kotlin Coroutines
* world
*/
class HelloKotlin5 {
}
2 协程构建器内部隐藏的CoroutineScope实例
import kotlinx.coroutines.*
/**
* 协程构建器内部隐藏的CoroutineScope实例
* 外部协程依赖由外部协程创建的内部协程(不是其他协程构建器创建的内部协程)完成
*/
/**
* 每一个协程构建器(包括runBlocking)都会向其代码块作用域内部添加一个CoroutineScope实例。我们可以在该作用域中启动协程,而无需
* 显式地将其join到一起,这是因为外部协程(在下面的例子中就是runBlocking创建的协程)会等待该作用域中所有**由外部协程启动的子协程**全部完成后才会完成
*/
fun main() = runBlocking {
// (每一个协程构建器都会向其代码块作用域内部添加一个CoroutineScope实例)
// 从runBlocking的外部协程 创建新的子协程 这种情况下 外部协程依赖内部协程执行结束
/*GlobalScope.*/launch {//注意对比加不加GlobalScope.的情况
delay(1000)
println("Kotlin Coroutines")
}
println("hello")
}
/**
* 输出
* hello
* Kotlin Coroutines
*/
class HelloKotlin6 {
}
3 通过coroutineScope builder来声明自己的协程作用域
/**
* 通过coroutineScope builder来声明自己的协程作用域
*/
/**
* 除去不同的协程构建器(如runBlocking launch)所提供的协程作用域(coroutine scope)外,我们还可以通过coroutineScope builder来声明自己的协程作用域.
* 该构建器会创建一个协程作用域,并且会等待所有启动的子协程全部完成后自身才会完成。
*
* runBlocking与coroutineScope的主要区别是 coroutineScope在等待所有子协程完成Job时不会阻塞当前的线程
*/
fun main() = runBlocking {
launch {
delay(1000) // 不阻塞线程 计时1s
println("my job1")
}
println("person")// 第一个输出
// 通过coroutineScope builder来声明自己的协程作用域
// 只有所有的子协程完成 它自己才会退出 有点类似join(coroutineScope 也是一个挂起函数)
coroutineScope {
launch {// 创建新的子协程 计时3s
delay(3000)
println("my job2")
}
delay(2000)// 计时2s
println("hello world") // 第二个输出
}
println("welcome")// 最后一个输出
// 其余按照delay时间 间隔1s依次输出
}
/**
* 输出
* person
* my job1
* hello world
* my job2
* welcome
*/
class HelloKotlin7 {
}
4 runBlocking vs coroutineScope
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
/**
* runBlocking vs coroutineScope
* 通过coroutineScope builder来声明自己的协程作用域
*/
/**
* 除去不同的协程构建器(如runBlocking launch)所提供的协程作用域(coroutine scope)外,我们还可以通过coroutineScope builder来声明自己的协程作用域.
* 该构建器会创建一个协程作用域,并且会等待所有启动的子协程全部完成后自身才会完成。
*
* runBlocking与coroutineScope的主要区别是 coroutineScope在等待所有子协程完成Job时不会阻塞当前的线程。
*
* 1.runBlocking并非挂起函数;也就是说,调用它的线程会一直位于该函数中,直到协程执行完毕
* 2.coroutineScope是挂起函数;也就是说,如果其中的协程挂起。那么coroutineScope函数也会挂起。这样,创建coroutineScope的外层
* 函数就可以继续在同一个线程中执行了,该线程会“逃离”coroutineScope之外,并且可以做其他事情
*/
fun main() = runBlocking {
launch {
delay(1000) // 不阻塞线程 计时1s
println("my job1")
}
println("person")// 第一个输出
// 通过coroutineScope builder来声明自己的协程作用域
// 只有所有的子协程完成 它自己才会退出 有点类似join(coroutineScope 也是一个挂起函数)
coroutineScope {
launch {// 创建新的子协程 计时3s
delay(3000)
println("my job2")
}
delay(2000)// 计时2s
println("hello world") // 第二个输出
}
println("welcome")// 最后一个输出
// 其余按照delay时间 间隔1s依次输出
}
/**
* 输出
* person
* my job1
* hello world
* my job2
* welcome
*
* person立即输出
* my job1 间隔1s后输出
* hello world 间隔1s后输出
* my job2 间隔1s后输出
* welcome和my job2同时输出
*/
class HelloKotlin7_2 {
}
5 协程 vs 线程
// HelloKotlin8.kt
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
/**
* 协程是轻量级的
* 对比HelloKotlin8和HelloKotlin9可以看出为什么说协程是轻量级的(相比于线程)
* 注意这里代码和视频略有不同 增加了计算时间的逻辑 然而这个只是大致统计时间 因为最后一次线程或者协程不一定
* 是最后一个执行 不过仍然能够看到明显的效果
*
* 视频中线程创建超过1万个 JMV就会内存溢出 而协程创建超过一万个却没事
* 在我的电脑上现象不一样 线程和协程无论创建多少个 都不会内存溢出
* 但是当数目大于20000时 会发现协程的执行速度远大于线程 不过不同的电脑可能现象不一样
*
*/
fun main() = runBlocking {
val start = System.currentTimeMillis()
val repeatTimes = 20000
repeat(repeatTimes) {
// 开启100个协程 协程等待1s后输出A
launch {
delay(100)
println("A")
if (it == repeatTimes - 1) {
val totalTime = System.currentTimeMillis() - start
println("coroutine $repeatTimes cost time $totalTime")
}
}
}
println("hello world")
}
class HelloKotlin8 {
}
// HelloKotlin9.kt
import java.lang.Thread.sleep
fun main() {
val start = System.currentTimeMillis()
val repeatTimes = 20000
repeat(repeatTimes) {
// 开启100个线程 线程等待1s后输出A
thread {
sleep(100)
println("A")
if (it == repeatTimes -1){
val totalTime = System.currentTimeMillis() - start
println("thread $repeatTimes cost time $totalTime")
}
}
}
println("hello world")
}
class HelloKotlin9 {
}