想知道是否有人可以提供帮助,我正在尝试了解使用两个线程访问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) }
}
}
如果要锁定集合或任何对象以进行并发访问,则可以使用与Java的synchronized
关键字几乎相同的结构。
因此,在访问此类对象时,您会这样做
fun someFun() {
synchronized(yourCollection) {
}
}
您还可以使用Java的synchronizedCollection
类中的Collections
方法,但这仅使单方法访问线程安全,如果必须通过iterate
进行collection
,则仍然必须手动处理同步。
您仅同步获取器和设置器,因此,当您开始使用引用时,您已经进入列表,它已经被解锁。
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) } }
}
}