Kotlin学习笔记22 协程part2 join CoroutineScope 协程vs线程

参考链接

示例来自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 {
}

你可能感兴趣的:(Kotlin,kotlin)