持续创作,加速成长!这是我参与「掘金日新计划 · 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最经典的一个应用场景
下载的大致流程图
什么是冷流什么是热流
Flow是冷流,简单来说,如果Flow有流订阅者Collector以后,发射出来的值才会实实在在的存在于内存之中,这跟懒加载的概念很像,相对于的热流StateFlow和SharedFlow是热流,在垃圾回收之前,都是存在内存之中,并且处于活跃状态的。
StateFlow
StateFlow 是一个状态容器式可观察数据流,可以向其收集器发出当前状态更新和新状态更新,还可以通过其value属性读取当前状态值。
SharedFlow
SharedFlow会向从其中收集值的所有使用方发出数据。