Android Kotlin(六)协程的并发问题

书接上回:Android Kotlin知识汇总(三)Kotlin 协程 

协程的并发问题

在一个协程中,循环创建10个子协程且单独运行各自Default线程中,并让每个子协程对变量 i 进行1000次自增操作。示例如下:

fun main() = runBlocking {
    var i = 0
    repeat(10) {
        val job = launch(Dispatchers.Default) {
            repeat(1000) {
                i++
            }
        }
    }    
    println("i: $i")
}
/*
输出信息:i: 9310
 */

此时,这10个协程运行在不同的线程中,可能会出现并发问题,最终结果小于、等于10000。


解决并发问题 

我们知道,在 Java 中最简单的同步方式是 synchronized、Atomic、Lock等同步手段。事实上,在Kotlin 协程中也是可以同样适用这些同步手段的。

Kotlin特性之一:与 Java 的互操作性。由于 Kotlin 代码可编译为 JVM 字节码,意味着 Kotlin 利用现有的 Java 库直接调用。


Synchronized

使用 @Synchronized 注解修饰函数或 synchronized(){} 

​fun main() = runBlocking {
    var i = 0
    @Synchronized
    fun add() {
        i++
    }
    repeat(10) {
        val job = launch(Dispatchers.Default) {
            repeat(1000) {
                add()
            }
        }
    }    
    println("i: $i")
}
//输出信息:i: 10000
synchronized 问题

虽然 Kotlin 协程是基于 Java 线程的,但是它已经脱离了 Java 原本的范畴。

如果在 synchronized(){} 中调用suspend挂起函数,编译器会报错。

挂起函数会被翻译为 Continuation 的异步函数,造成 synchronized 代码块无法处理同步。

  suspend fun add() {
        i++
  }

Mutex 同步锁

因为是Java 的锁是阻塞式的,会影响协程的非阻塞式特性,所以在 Kotlin 协程中,不推荐使用 Java 中的同步锁。

Kotlin 官方提供了非阻塞式的锁:Mutex。

public interface Mutex {
    public val isLocked: Boolean
  
    public suspend fun lock(owner: Any? = null)

    public fun unlock(owner: Any? = null)
}

Mutex 是一个接口,lock() 方法是一个挂起函数,支持挂起和恢复,这是一个非阻塞式同步锁。

为了简化try、catch、lock、unlock的模板代码,Mutex提供了withLock{} 扩展函数

public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
    lock(owner)
    try {
        return action()
    } finally {
        unlock(owner)
    }
}

使用示例如下:

fun main() = runBlocking {
    val mutex = Mutex()
    var i = 0
    repeat(10) {
        val job = launch(Dispatchers.Default) {
            repeat(1000) {
                //模板代码
                try {
                    mutex.lock()
                    i++
                } catch (e: Exception) {
                    println(e)
                } finally {
                    mutex.unlock()
                }
                //简化代码
                mutex.withLock {
                    i++
                }
            }
        }
    }
    println("i: $i")
}
//输出信息:i: 10000


另外还有Actor 并发同步方式,本质是 Channel管道消息的简单封装。在 actor{} 外部,发送了10000次 AddMsg 消息,最后发送一次 ResultMsg,获取计算结果。


  

 

相关推荐

  1. 优雅控制(goroutine)并发数量

    2024-03-23 08:22:01       32 阅读
  2. 使用库httpx并发请求

    2024-03-23 08:22:01       17 阅读
  3. 库——面试问题

    2024-03-23 08:22:01       5 阅读
  4. FREERTOS

    2024-03-23 08:22:01       11 阅读
  5. Go 之缓冲通道限制并发数目

    2024-03-23 08:22:01       13 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-23 08:22:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-23 08:22:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-23 08:22:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-23 08:22:01       18 阅读

热门阅读

  1. linux正则表达式之^

    2024-03-23 08:22:01       30 阅读
  2. nginx有哪些安装方法

    2024-03-23 08:22:01       18 阅读
  3. TCP与UDP:网络协议的技术原理与要点

    2024-03-23 08:22:01       17 阅读
  4. Docker搭建LNMP环境实战(一):前言

    2024-03-23 08:22:01       19 阅读
  5. 探索自然语言处理:从入门到精通的完整指南

    2024-03-23 08:22:01       22 阅读
  6. 单例模式的实现方式

    2024-03-23 08:22:01       19 阅读
  7. 懒汉式【单例模式】

    2024-03-23 08:22:01       17 阅读
  8. 设计模式(行为型设计模式——状态模式)

    2024-03-23 08:22:01       14 阅读
  9. 【PHP】通过PHP开启/暂停Apache、MySQL或其他服务

    2024-03-23 08:22:01       21 阅读
  10. OpenCV基于阈值的分割技术详细介绍

    2024-03-23 08:22:01       18 阅读
  11. 数仓建模架构—Inmon范式建模与Kimball维度建模

    2024-03-23 08:22:01       20 阅读