当前位置:   article > 正文

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

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

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

协程的并发问题

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

  1. fun main() = runBlocking {
  2. var i = 0
  3. repeat(10) {
  4. val job = launch(Dispatchers.Default) {
  5. repeat(1000) {
  6. i++
  7. }
  8. }
  9. }
  10. println("i: $i")
  11. }
  12. /*
  13. 输出信息:i: 9310
  14. */

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


解决并发问题 

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

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


Synchronized

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

  1. fun main() = runBlocking {
  2. var i = 0
  3. @Synchronized
  4. fun add() {
  5. i++
  6. }
  7. repeat(10) {
  8. val job = launch(Dispatchers.Default) {
  9. repeat(1000) {
  10. add()
  11. }
  12. }
  13. }
  14. println("i: $i")
  15. }
  16. //输出信息:i: 10000
synchronized 问题

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

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

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

  1. suspend fun add() {
  2. i++
  3. }

Mutex 同步锁

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

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

  1. public interface Mutex {
  2. public val isLocked: Boolean
  3. public suspend fun lock(owner: Any? = null)
  4. public fun unlock(owner: Any? = null)
  5. }

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

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

  1. public suspend inline fun <T> Mutex.withLock(owner: Any? = null, action: () -> T): T {
  2. lock(owner)
  3. try {
  4. return action()
  5. } finally {
  6. unlock(owner)
  7. }
  8. }

使用示例如下:

  1. fun main() = runBlocking {
  2. val mutex = Mutex()
  3. var i = 0
  4. repeat(10) {
  5. val job = launch(Dispatchers.Default) {
  6. repeat(1000) {
  7. //模板代码
  8. try {
  9. mutex.lock()
  10. i++
  11. } catch (e: Exception) {
  12. println(e)
  13. } finally {
  14. mutex.unlock()
  15. }
  16. //简化代码
  17. mutex.withLock {
  18. i++
  19. }
  20. }
  21. }
  22. }
  23. println("i: $i")
  24. }
  25. //输出信息:i: 10000

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

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/花生_TL007/article/detail/316460
推荐阅读
相关标签
  

闽ICP备14008679号