kotlin<第十二篇>:协程并发安全

(1)不安全的并发访问

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

runBlocking {

    var count = 0
    List(1000) {
        GlobalScope.launch { count++ }
    }.joinAll()
    println(count)

}

打印出来的count值并不是1000。

(2)协程并发安全

在Java中,提供了原子性的对象:AtomicInteger,可以保证协程的安全性:

runBlocking {
    var count = AtomicInteger(0)
    List(1000) {
        GlobalScope.launch { count.incrementAndGet()}
    }.joinAll()
    println(count)
}

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

  • Channel:并发安全的消息通道;
  • Mutex:轻量级锁,它的lock和unlock从语义上与线程锁比较类似,之所以轻量级是因为它在获取不到锁时不会阻塞线程,二十挂起等待锁的释放。
  • Semaphore:轻量级信号量,信号量可以有多个,协程在获取到信号量后即可执行并发操作。当Semaphore的参数为1时,效果等价于Mutext。

Mutex演示:

runBlocking {
    var count = 0
    val mutex = Mutex()
    List(1000) {
        GlobalScope.launch {
            mutex.withLock { count++ }
        }
    }.joinAll()
    println(count)
}

Semaphore演示:

runBlocking {
    var count = 0
    val semaphore = Semaphore(1)
    List(1000) {
        GlobalScope.launch {
            semaphore.withPermit { count++ }
        }
    }.joinAll()
    println(count)
}
(3)避免访问外部可变状态

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

runBlocking {
    var count = 0
    var result = count + List(1000) {
        GlobalScope.async { 1 }
    }.map { it.await() }.sum()
    println(result)
}

[完...]

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容