Retrofit配合协程发起网络请求的线程安全性及优化方案咨询
关于线程安全性的问题解答
你的原有认知存在错误,当前实现不具备线程安全性,风险点如下:
- Kotlin默认创建的普通对象、集合都没有内置线程安全保障,你使用的
mutableListOf底层是Java的ArrayList,属于非线程安全集合,多线程并发调用add方法时,可能出现元素丢失、数组越界、长度统计异常等问题。 - 第三方库内部使用
Dispatchers.IO调度网络请求,回调方法onResult会在IO线程池的不同子线程执行,不会自动为你的handler实例加锁,完全可能出现多个请求同时执行add操作、同时读取列表长度的竞态场景,极端情况下会出现结果处理逻辑被多次触发、结果列表元素缺失的问题。
更优的业务实现方案
推荐使用协程的回调转挂起能力,将第三方库的回调式API改造为挂起函数,再通过协程并发原语统一管理请求和结果,完全规避手动维护集合、计数的线程安全问题,实现代码如下:
- 封装回调API为挂起函数
suspend fun checkUserSuspend(userId: String): UserCheckResult? = suspendCoroutine { continuation -> checkUser(userId, object : UserCheckResultHandler { override fun onResult(userCheckResult: UserCheckResult?) { continuation.resume(userCheckResult) } }) }
- 并发请求并统一收集结果
val userList: List<String> = getUsers() ?: return // 可根据业务场景替换为对应的协程作用域,如viewModelScope、lifecycleScope coroutineScope { // 批量创建异步请求任务 val requestTasks = userList.map { userId -> async(Dispatchers.IO) { checkUserSuspend(userId) } } // 等待所有请求完成,过滤无效空结果 val allValidResults = requestTasks.awaitAll().filterNotNull() // 统一处理全部结果 doSomethingWithResultList(allValidResults) print("SUCCESS") }
该方案的优势:
- 无需手动操作非线程安全集合、无需自行计数判断请求完成状态,协程框架会保证结果收集的安全性,
awaitAll会自动等待所有任务执行完毕。 - 可灵活拓展超时、取消、异常处理逻辑,比如配置单个请求超时时间、部分请求失败时取消所有剩余任务等。
如果要兼容原有回调实现,也可以临时将结果列表替换为线程安全实现,比如val userCheckResultList = Collections.synchronizedList(mutableListOf<UserCheckResult>()),同时给onResult的逻辑加同步锁,但该方案的可维护性、扩展性远低于协程原生方案。
内容的提问来源于stack exchange,提问作者Igor
相关产品推荐
相关产品推荐

