【问题标题】:Kotlin - How to lock a collection when accessing it from two threadsKotlin - 从两个线程访问集合时如何锁定集合
【发布时间】:2020-02-09 14:33:10
【问题描述】:

想知道是否有人可以提供帮助,我正在尝试了解在 Kotlin 中使用两个线程访问集合的正确方法。

下面的代码模拟了我在实时系统中遇到的问题。一个线程遍历集合,但另一个线程可以删除该数组中的元素。

我已尝试将 @synchronized 添加到集合 getter,但这仍然给我一个并发修改异常。

谁能告诉我正确的做法是什么?

class ListTest() {

    val myList = mutableListOf<String>()
        @Synchronized
        get() = field

    init {
        repeat(10000) {
            myList.add("stuff: $it")
        }
    }
}

fun main() = runBlocking<Unit> {

    val listTest = ListTest()

    launch(Dispatchers.Default) {
        delay(1L)
        listTest.myList.remove("stuff: 54")
    }

    launch {
        listTest.myList.forEach { println(it) }
    }
}

【问题讨论】:

  • 你应该避免像这样共享可变状态。你真正想解决什么问题?也许你正在寻找一个«演员»?
  • 即使同步访问列表,forEach 隐式创建的迭代器在调用remove 后也会失效。

标签: kotlin concurrency synchronized


【解决方案1】:

你只是在同步 getter 和 setter,所以当你开始使用你得到的列表的引用时,它已经被解锁了。

Kotlin 有 Mutex 类可用于对共享可变对象进行锁定操作。 Mutex 比 Java 的 synchronized 更好,因为它暂停而不是阻塞协程线程。

您的示例在现实世界中的设计很糟糕,因为您的类公开了一个可变列表。但是要确保修改列表至少是安全的:

class ListTest() {

    private val myListMutex = Mutex()

    private val myList = mutableListOf<String>()

    init {
        repeat(10000) {
            myList.add("stuff: $it")
        }
    }

    suspend fun modifyMyList(block: MutableList<String>.() -> Unit) {
        myListMutex.withLock { myList.block() }
    }
}

fun main() = runBlocking<Unit> {

    val listTest = ListTest()

    launch(Dispatchers.Default) {
        delay(1L)
        listTest.modifyMyList { it.remove("stuff: 54") }
    }

    launch {
        listTest.modifyMyList { it.forEach { println(it) } }
    }
}

如果你不使用协程,而不是 Mutex(),你可以使用 Any 而不是 withLock 使用 synchronized (myListLock) {},就像在 Java 中一样,以防止同步块中的代码同时运行。

【讨论】:

    【解决方案2】:

    如果您想锁定一个集合或任何对象以进行并发访问,您可以使用与 java 的 synchronized 关键字几乎相同的构造。

    所以在访问这样的对象时,你会这样做

    fun someFun() {
        synchronized(yourCollection) {
    
        }
    }
    

    你也可以使用java的Collections类中的synchronizedCollection方法,但这只会使单方法访问线程安全,如果你必须iterate超过collection,你仍然需要手动处理同步.

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2020-05-25
      • 2014-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多