Kotlin - 从两个线程访问集合时如何锁定集合

MVP*_*MVP 7 concurrency synchronized kotlin

想知道是否有人可以提供帮助,我正在尝试了解使用两个线程访问 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) }
    }
}
Run Code Online (Sandbox Code Playgroud)

Ten*_*r04 11

您只是同步 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) } }
    }
}
Run Code Online (Sandbox Code Playgroud)

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