11kotin之协程(三)

kotlin之协程(三)

1.CoroutineStart几种状态模式

默认是CoroutineStart.DEFAULT 立即执行

LAZY:稍后执行,只有执行job.start/join或者await才会开始执行

package day8Coroutines

import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.async
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import kotlin.system.measureTimeMillis

/**
 *  CoroutineStart.DEFAULT 立即执行
 *  LAZY稍后执行
 *
 *
 */
/**
 * LAZY:稍后执行,只有执行job.start/join或者await才会开始执行
 *   job.start或者await两者执行的时间不一致,await会阻塞协程
 *
 */
fun main(args: Array<String>) {
    runBlocking {
        val time = measureTimeMillis {
            val v1 = async(start = CoroutineStart.LAZY) {
                initValue()
            }
            val v2 = async(start = CoroutineStart.LAZY) {
                initValue2()
            }
            v1.start()
            v2.start()
            //实现了并发,获取值
            var result1 = v1.await()
            var result2 = v2.await()
            println("${result1 + result2}")
        }
    }
}

private suspend fun initValue(): Int {
    delay(2000)
    return 20
}

private suspend fun initValue2(): Int {
    delay(2000)
    return 50
}

2.async异步案例

package day8Coroutines

import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking

fun main() {
    runBlocking {
        var v = intSum()
        print(v)
    }
}

private suspend fun intSum(): Int = coroutineScope { // lamada表达式,最后一项作为返回值
    val v1 = async { initValue() }
    val v2 = async { initValue2() }
//    return@coroutineScope v1.await() + v2.await()
    v1.await() + v2.await()
}

private suspend fun initValue(): Int {
    delay(2000)
    return 20
}

private suspend fun initValue2(): Int {
    delay(2000)
    return 50
}

3.父子协程异常与取消问题

协程的取消总是会沿着协程层次体系向上运行传递。简单说,当前协程出现了问题,则会取消当前协程,继续父的协程
package day8Coroutines

import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.runBlocking
import java.lang.Exception

/**
 * 父子协程的异常和取消问题
 * 协程的取消总是会沿着协程层次体系向上运行传递
 *
 */
fun main(args: Array<String>) {
    runBlocking {
        try {
            failureComputation()
        } finally {
            println("结束")
        }
    }
}

private suspend fun failureComputation(): Int = coroutineScope {
    val v1 = async<Int> {
        delay(1000000)
        50
    }
    val v2 = async<Int> {
        Thread.sleep(2000)
        throw Exception()
    }
    v1.await() + v2.await()
}

4.协程与线程之间的关系

简单说:默认会获取父协程的上下文CoroutineContext,如果是主线程,则主线程中的协程就在主线程中。 当然,Global和自行定义的另外说。

package day8Coroutines

import kotlinx.coroutines.*
import java.util.concurrent.Executors


/**
 * 协程与线程之间的关系
 * 协程上下文与分发器(Coroutine Context与Dispatcher)
 * 协程总是会在某个上下文执行,实际是由CoroutineContext类型的一个实例来表示的,
 * 协程本质是各种元素所构成的一个集合,主要包括job以及分发器 CoroutineDispatcher
 * CoroutineDispatcher作用,可以将协程指定到一个具体的线程中执行
 *
 *
 * 注意:如果不加任何限制Unified,具体在哪个线程执行时不确定的
 *
 *
 * 注意:默认会获取父协程的上下文CoroutineContext,默认是EmptyCoroutineContext,它默认在共享线程池中
 *      但如果有父协程,则如果不明确说明,默认获取父协程的上下文,在父协程的线程中执行
 *
 */
fun main(args: Array<String>) {
    runBlocking {
        GlobalScope.launch { //后台线程池
            println("5:${Thread.currentThread().name}")
        }
        launch {//main
            println("1:${Thread.currentThread().name}")
        }
        launch(Dispatchers.Unconfined) {//不确定
            println("2:${Thread.currentThread().name}")
        }
        launch(Dispatchers.Default) { //共享线程池
            println("3:${Thread.currentThread().name}")
        }
        launch(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) {//main
            println("4:${Thread.currentThread().name}")
        }
    }
}

5.Dispatchers分发器

package day8Coroutines

import kotlinx.coroutines.*
import java.util.concurrent.Executors

/**
 * CoroutineDispatcher作用,可以将协程指定到一个具体的线程中执行
 *
 * Dispatchers.Unconfined 不定义具体哪个线程执行
 * Dispatchers.Default  共享线程中执行
 * Dispatchers.main  主线程中执行
 * Dispatchers.IO  是为将阻塞的IO任务卸载到共享线程池而设计的
 *
 *
 * * 注意:默认会获取父协程的上下文CoroutineContext,默认是EmptyCoroutineContext,它默认在共享线程池中
 *      但如果有父协程,则如果不明确说明,默认获取父协程的上下文,在父协程的线程中执行
 *
 */
fun main(args: Array<String>) {
    runBlocking {
        GlobalScope.launch {
            println("5:${Thread.currentThread().name}")
        }
        launch {
            println("1:${Thread.currentThread().name}")
        }
        launch(Dispatchers.Unconfined) {
            println("2:${Thread.currentThread().name}")
        }
        launch(Dispatchers.Default) {
            println("3:${Thread.currentThread().name}")
        }
        launch(Executors.newSingleThreadExecutor().asCoroutineDispatcher()) {
            println("4:${Thread.currentThread().name}")
        }
    }
}

6.Dispatchers.Unconfined

刚刚,我们说,Dispatchers.Unconfined的所在的线程是不确定的。 其实不对,我们是可以分析出来的。

Dispatchers.Unconfined 仅仅持续第一个线程,一旦持续到第一个挂起函数,则具体会到哪个线程执行是不确定的,是在挂起函数所在线程(默认default)
package day8Coroutines

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking

/**
 * Dispatchers.Unconfined 仅仅持续第一个线程,一旦持续到第一个挂起函数,则具体会到哪个线程执行是不确定的,是在挂起函数所在线程(默认default)
 *
 * 因此,它基本上不被使用
 */
fun main(args: Array<String>) {
    runBlocking {
        launch(Dispatchers.Unconfined) {
            println("1:${Thread.currentThread().name}")
            delay(100)
            println("2:${Thread.currentThread().name}")
        }
        launch {
            println("3:${Thread.currentThread().name}")
            delay(2000)
            println("4:${Thread.currentThread().name}")
        }
    }
}

1:main
3:main
2:kotlinx.coroutines.DefaultExecutor
4:main

7.协程在线程中切换

Android kotlin协程实际上是一个线程框架,底层是通过线程池来实现的,但是比线程更加灵活,由于比线程多了一个上下文,所以后台任务执行完毕后可以自动的切换回上下文协程中。这是线程比较难实现的。

简单说:就是通过上下文来实现线程的切换

package day8Coroutines

import kotlinx.coroutines.*

/**
 * 协程在线程中切换
 * CoroutineContext:四种协程上下文Unconfined、Default、newSingleThreadContext、runBlocking,此时使用Default调度器,使用一个JVM内的共享线程池
 * 上下文:单线程池中执行,如果使用newSingleThreadContext.use线程池会自动关闭
 */
fun main(args: Array<String>) {
    newSingleThreadContext("Context1").use { ctx1 ->
        newSingleThreadContext("Context2").use { ctx2 ->
            runBlocking {
                logger("Start")
                withContext(ctx2) {
                    logger("work in Context2")
                }
                logger("Back to Context")
            }
        }
    }
    println("你好")

    runBlocking {
        GlobalScope.launch(newSingleThreadContext("hello")) {
            println("你好2")
        }
    }
}
private fun logger(logMessage: String) = println("${Thread.currentThread().name}  $logMessage")

8.协程中线程切换案例

withContext:不会创建新的协程,在指定协程上运行挂起代码块,并挂起该协程直至代码块运行完成。简单说,就是在已有(并非原有)协程上挂起。

package day8Coroutines

import kotlinx.coroutines.*


fun main(args: Array<String>) {
    runBlocking {
        initData()
    }
}

fun initData() {
    GlobalScope.launch(Dispatchers.Main) {
        ioCode1() //耗时操作1
//        uiCode1() //更新UI操作1
        ioCode2() //耗时操作2
//        uiCode2() //更新UI操作2
        ioCode3() //耗时操作3
//        uiCode3() //更新UI操作3
    }
}


suspend fun ioCode1() {
    withContext(Dispatchers.IO) {
        println("我是IO线程1==${Thread.currentThread().name}")
    }
}

suspend fun ioCode2() {
    withContext(Dispatchers.IO) {
        println("我是IO线程2==${Thread.currentThread().name}")
    }
}

suspend fun ioCode3() {
    withContext(Dispatchers.IO) {
        println("我是IO线程3==${Thread.currentThread().name}")
    }
}

9.Job详解

package day8Coroutines

import kotlinx.coroutines.Job
import kotlinx.coroutines.isActive
import kotlinx.coroutines.runBlocking

/**
 * 如果获取上下文中job
 * 协程的job是归属于上下文(Context)的一部分,kotlin为我们提供了一种手段来通过协程上下文获取到协程自身的JOB对象
 * 我们可以通过coroutinesContext[Job]表达式来获取上下文中的job对象
 *
 *  Job其实使用地是半生对象中,由于半生对象在kotlin中可以被隐藏持有
 * 通过转换为java:
 * Job job = (Job)$this$runBlocking.getCoroutineContext().get((Key)Job.Key);
Job job2 = (Job)$this$runBlocking.getCoroutineContext().get((Key)Job.Key);
 */
fun main(args: Array<String>) {
    runBlocking {
        val job: Job? = coroutineContext[Job]
        val job2: Job? = coroutineContext[Job.Key]
        println(job == job2)
        println(job2?.isActive)
        println(coroutineContext.isActive)
        println(coroutineContext[Job]?.isActive)
        println(job?.isActive)
    }
}

10父子协程之间的关系

GlobalScope:特殊列外,其Job没有顶级父Job,看源码可以知道,它不会绑定到其他任何的Job中,所以是独立的协程,可以独立运行

其他父子协程:子协程的Job会成为父协程Job的一个孩子,当父协程取消时,子协程会通过递归方式一并取消子协程。

另外,如果不是手动取消的话,父协程,总是会等待子协程完毕之后才会结束。

package day8Coroutines

import kotlinx.coroutines.*

/**
 * 父子协程关系:
 * 1.当一个协程通过另外一个协程的CoroutineScope作用域来启动的,那么这个协程就会通过CoroutineScope.coroutineeContext来继承上下文
 * 2.同时,新的协程的Job就会成为父协程Job的一个孩子,
 * 3.所以,父协程取消时,子协程通过递归的方式一并取消
 * 4.父协程,总是会等待子协程完毕之后才会结束,注意GlobalScope除外
 *
 * 特殊情况:GlobalScope启动的,其Job没有顶级父Job,看源码可以知道,它不会绑定到其他任何的Job中,所以是独立的协程,可以独立运行
 * 所以官方建议:GlobalScope一般作为顶级Job来使用,而不是在某Job中使用
 */
fun main(args: Array<String>) {
    runBlocking {
        var request = launch {
            GlobalScope.launch { //不受request控制
                delay(2000)
                println("顶级Job,不绑定任何Job中")
            }
            launch {
                delay(500)
                println("子Job")
                delay(1000)
                println("子job2")
            }
            launch(Dispatchers.Default) {
                delay(2500)
                println("子Default job2")
            }
        }
        delay(1000)
        request.cancelAndJoin()
        delay(3000)
    }
}

11.CoroutineName对协程进行命名

package day8Coroutines

import kotlinx.coroutines.*

/**
 * CoroutineName对协程进行重新命名,更好输出日志
 * CoroutineName 继承AbstractCoroutineContextElement,也是上下文
 */
fun main(args: Array<String>) {
    runBlocking(CoroutineName("main")) {
        val v = async(CoroutineName("coroutine")) {
            delay(800)
            println("你好")
            30
        }
        launch(Dispatchers.Default) { }
        launch(Dispatchers.Unconfined) { }
        launch(newSingleThreadContext("context1")) {}
//        launch(Dispatchers.Main) { } //在android中使用
        launch(Dispatchers.IO) { }
        println(v.await())
    }
}

12.其他小知识

ensureActive() 在协程不在 active 状态时会立即抛出异常,所以也可以这样做: 针对密集型计算


while (i < 5) {
    ensureActive()}
        println("你好")
        30
    }
    launch(Dispatchers.Default) { }
    launch(Dispatchers.Unconfined) { }
    launch(newSingleThreadContext("context1")) {}

// launch(Dispatchers.Main) { } //在android中使用
launch(Dispatchers.IO) { }
println(v.await())
}
}


### 12.其他小知识

`ensureActive()` 在协程不在 active 状态时会立即抛出异常,所以也可以这样做: 针对密集型计算

```kotlin

while (i < 5) {
    ensureActive()
    …
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值