未使用循环仍触发java.util.ConcurrentModificationException问题排查
核心错误排查
问题本质
ConcurrentModificationException不是只有手动写循环才会触发——removeIf方法内部依赖迭代器遍历集合并执行删除操作。而你用mutableMapOf()默认生成的是LinkedHashMap,它的迭代器是**快速失败(fail-fast)**类型:如果迭代过程中集合被其他线程修改(迭代器自身的remove方法除外),就会抛出这个异常。
你的场景里,registerSession可能在业务线程执行添加操作,而SseEmitter的onTimeout/onError回调大概率在另一个线程触发。这就导致多线程并发修改同一个MutableSet:当removeIf正通过迭代器遍历集合时,其他线程执行found.add(sessionData)修改了集合结构,直接触发快速失败机制抛出异常。
关键验证点
- 确认
registerSession的调用线程和removeSessionForUser的执行线程是否为同一个 - 默认
mutableSetOf()返回的是LinkedHashSet,同样是fail-fast实现,不支持并发修改
解决方案
方案1:替换为线程安全集合
把普通集合换成线程安全的实现,从根源避免并发冲突:
// 使用ConcurrentHashMap保证Map层面线程安全 val userIdToSessionDataMap: MutableMap<String, MutableSet<UserSessionData>> = ConcurrentHashMap() fun registerSession(userId: String, jti: String): SseEmitter { val sseEmitter = SseEmitter(timeout) sseEmitter.onTimeout { removeSessionForUser(userId, jti) } sseEmitter.onError { removeSessionForUser(userId, jti) } val sessionData = UserSessionData(jti, sseEmitter) // 用CopyOnWriteArraySet替代普通Set,它通过复制快照实现线程安全 userIdToSessionDataMap.computeIfAbsent(userId) { CopyOnWriteArraySet() }.add(sessionData) return sseEmitter }
CopyOnWriteArraySet的迭代器遍历的是集合快照,不会因为其他线程修改集合而抛出异常。
方案2:手动加锁保护
如果不想替换集合,就在所有操作集合的逻辑外层加锁,保证同一时间只有一个线程能操作集合:
private val lock = Any() private fun removeSessionForUser(userId: String, jti: String) { synchronized(lock) { val found = userIdToSessionDataMap[userId] found?.removeIf { it.jti == jti } } } fun registerSession(userId: String, jti: String): SseEmitter { val sseEmitter = SseEmitter(timeout) sseEmitter.onTimeout { synchronized(lock) { removeSessionForUser(userId, jti) } } sseEmitter.onError { synchronized(lock) { removeSessionForUser(userId, jti) } } val sessionData = UserSessionData(jti, sseEmitter) synchronized(lock) { val found = userIdToSessionDataMap[userId] if (found != null) { found.add(sessionData) } else { userIdToSessionDataMap[userId] = mutableSetOf(sessionData) } } return sseEmitter }
通过synchronized锁保证集合操作的原子性,避免并发修改冲突。
内容的提问来源于stack exchange,提问作者NeverSleeps
相关产品推荐
相关产品推荐

