Kotlin - 协程学习的第9天

138 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情

并发安全

我们使用线程在解决并发问题的时候总是会遇到线程安全的问题,而java平台上的kotlin协程实现也无法免不了存在并发调度的情况。因此线程安全同样值得留意。

fun `testSyncSafe1`() = runBlocking {
    var count = 0;
    List(1000) {
        GlobalScope.launch { count++ }
    }.joinAll()

    println(count)
}

以上输出的结果肯定都是小于1000的,不是原子性的操作是线程不安全的

Java是提供的线程安全类
fun `test sync safe2`() = runBlocking {
    var count = AtomicInteger(0);
    List(1000) {
        GlobalScope.launch { count.incrementAndGet() }
    }

    println(count.get())
}
协程的并发工具

除了我们在线程中常用的解决并发问题的手段之外,协程框架也提供了一些并发安全的工具,包括:

  • Channel: 并发安全的消息通道,我们已经非常熟悉。
  • Metex:轻量级锁,它的lock和unlock从语义上与线程比较类似,之所以轻量是因为它在获取不到锁时不会阻赛线程,而是挂起等待锁的释放
fun testSyncMutex() = runBlocking {
    var count = 0;
    val mutex = Mutex()
    List(1000) {
        GlobalScope.launch {
            mutex.withLock {
                count++
            }
        }
    }.joinAll()

    println(count)
}
  • semaphore:轻量级信号量,信号量可以有多个,协程在获取信号量后即可执行并发操作。当semaphore的参数为1时,效果等价于Mutex
fun testSyncSemaphore() = runBlocking {
    var count = 0;
    val semaphore = Semaphore(1)
    List(1000) {
        GlobalScope.launch {
            semaphore.withPermit {
                count++
            }
        }
    }.joinAll()

    println(count)
}
避免访问外部可变状态

编写函数时要求它不得访问外部状态,只能基于参数做运算,通过返回值提供运算结果。

fun testSyncAvoid`() = runBlocking {
    var count = 0;
    count += List(1000) {
        GlobalScope.async {
            1
        }
    }.map {
        it.await()
    }.sum()

    println(count)
}

Flow 异步流

Flow最主要的作用在于异步返回多个值,文件下载就是Flow最经典的一个应用场景

image.png

下载的大致流程图

image.png

什么是冷流什么是热流

Flow是冷流,简单来说,如果Flow有流订阅者Collector以后,发射出来的值才会实实在在的存在于内存之中,这跟懒加载的概念很像,相对于的热流StateFlow和SharedFlow是热流,在垃圾回收之前,都是存在内存之中,并且处于活跃状态的。

StateFlow

StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新,还可以通过其value属性读取当前状态值。

SharedFlow

SharedFlow会向从其中收集值的所有使用方发出数据。