Java线程安全集合在Kotlin协程中是否依然安全?
Kotlin协程中使用Java线程安全队列的可行性分析
我和同事协作开发一款用Kotlin Coroutines并行向队列添加元素的应用,他的实现代码如下:
val queue = ArrayDeque<Deferred<ConnectorMessage>>() val queueMutationMutex = Mutex() myCoroutineScope.launch(Dispatchers.IO){ repeat(1000){ launch{ queueMutationMutex.withLock{ queue.add(foo) } } } }
我建议他改用Java线程安全队列以省去Mutex,但他认为Java并发数据结构的线程安全承诺仅针对不同线程的并发操作,而Kotlin协程可能在同一线程运行,因此这些结构不再适用。请问这种说法是否正确?
结论:这种说法不正确,Java线程安全队列完全适用于Kotlin协程的并发场景
具体原因如下:
- 协程是协作式调度:Kotlin协程在同一线程里执行时,只有当协程主动调用挂起函数时,才会把线程让给其他协程。而Java线程安全队列的
add、offer这类核心操作都是不会挂起的原子操作——执行过程中不会触发协程挂起,所以同一线程里的其他协程根本没机会在这些操作执行到一半的时候插进来,不存在操作被打断的问题。 - Java并发集合的安全保证不局限于多线程:Java线程安全队列的线程安全承诺,本质是保证不管多少线程访问,操作的原子性、数据可见性和执行顺序都没问题。哪怕协程跑在同一线程,只要多个协程是通过调度切换(比如挂起后恢复)来访问队列,这些队列的安全机制依然能生效,不会出现数据不一致的情况。
- 实际代码可以简化:如果换成
ConcurrentLinkedQueue这类Java线程安全队列,原来的代码完全可以去掉Mutex,简化成这样:
val queue = ConcurrentLinkedQueue<Deferred<ConnectorMessage>>() myCoroutineScope.launch(Dispatchers.IO){ repeat(1000){ launch{ queue.add(foo) } } }
内容的提问来源于stack exchange,提问作者J-bob
相关产品推荐
相关产品推荐

