线程安全问题排查
场景
偶现数据错乱,日志显示 ConcurrentModificationException 或列表数据不一致。
排查与方案
1. 常见线程安全问题
| 问题 | 场景 | 解决 |
|---|---|---|
| 集合并发修改 | 多线程同时读写 ArrayList | CopyOnWriteArrayList 或加锁 |
| SharedPreferences ANR | commit() 在主线程 | 改用 apply() 或 DataStore |
| LiveData 多线程 setValue | 非主线程调用 setValue() | 用 postValue() |
| 单例初始化竞争 | 多线程同时创建单例 | DCL + @Volatile |
| 回调线程不确定 | 网络回调可能在子线程 | withContext(Dispatchers.Main) |
2. 排查工具
// 开发模式开启 StrictMode 检测主线程违规
if (BuildConfig.DEBUG) {
StrictMode.setThreadPolicy(
StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.penaltyLog()
.build()
)
}
3. 修复示例
// ❌ 并发修改集合
class EventManager {
private val listeners = mutableListOf<Listener>()
fun add(l: Listener) { listeners.add(l) } // 线程 A
fun notify() {
listeners.forEach { it.onEvent() } // 线程 B → ConcurrentModificationException
}
}
// ✅ 修复方案 1:CopyOnWriteArrayList(读多写少)
class EventManager {
private val listeners = CopyOnWriteArrayList<Listener>()
fun add(l: Listener) { listeners.add(l) }
fun notify() { listeners.forEach { it.onEvent() } }
}
// ✅ 修复方案 2:协程 + Mutex(写多场景)
class EventManager {
private val mutex = Mutex()
private val listeners = mutableListOf<Listener>()
suspend fun add(l: Listener) = mutex.withLock { listeners.add(l) }
suspend fun notify() {
val snapshot = mutex.withLock { listeners.toList() }
snapshot.forEach { it.onEvent() }
}
}
面试答题要点
ConcurrentModificationException原因和修复方案- Kotlin Coroutine 场景用
Mutex替代synchronized - LiveData 的
setValuevspostValue线程要求 - 用 StrictMode 提前发现主线程违规