跳到主要内容

线程安全问题排查

场景

偶现数据错乱,日志显示 ConcurrentModificationException 或列表数据不一致。

排查与方案

1. 常见线程安全问题

问题场景解决
集合并发修改多线程同时读写 ArrayListCopyOnWriteArrayList 或加锁
SharedPreferences ANRcommit() 在主线程改用 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() }
}
}

面试答题要点

  1. ConcurrentModificationException 原因和修复方案
  2. Kotlin Coroutine 场景用 Mutex 替代 synchronized
  3. LiveData 的 setValue vs postValue 线程要求
  4. 用 StrictMode 提前发现主线程违规

相关链接