真实面经题目 · 原创解析
多线程如何防止并发问题?
这题考并发安全的系统化处理,回答要先减少共享,再对必须共享的状态保证原子性、可见性和有序性。
真实面经题目 · 原创解析
这题考并发安全的系统化处理,回答要先减少共享,再对必须共享的状态保证原子性、可见性和有序性。
我会先找共享可变状态、复合操作和对象发布点。最优先的办法是减少共享,比如不可变对象、线程封闭、ThreadLocal、消息队列或 actor 模型;必须共享时,再根据读写模式选择 synchronized、ReentrantLock、读写锁、CAS、原子类、并发容器或数据库乐观锁。并发安全不仅是互斥,还包括内存可见性、指令有序性、幂等和死锁规避,工程上还要控制锁粒度、持锁时间和压测验证。
并发问题通常来自多个线程读写同一份可变状态,尤其是 check-then-act、读改写、跨多个变量的不变式和未安全发布的对象。先定位临界区比直接加锁更重要。
能不共享就不共享。不可变对象、局部变量、线程封闭、拷贝快照、消息传递和任务串行化都能从设计上降低并发复杂度。
需要共享时,互斥锁适合保护复杂临界区,读写锁适合读多写少,CAS 和原子类适合简单原子更新,并发容器适合常见集合操作。选择要看冲突概率、临界区大小和可维护性。
线程安全不只是不同时写。volatile、锁释放获取、final 安全发布和并发容器的 happens-before 关系,决定一个线程的修改能否被另一个线程正确看到。
锁粒度过大会降低吞吐,锁顺序混乱会死锁,持锁执行 I/O 会放大延迟。实际系统还要有超时、降级、压测、竞态测试和生产指标来验证并发设计。
它能保证可见性和一定有序性,但不能让 i++ 这类复合操作变成原子操作,多变量不变式仍需要锁或其他同步。
固定加锁顺序,减少嵌套锁,避免持锁调用外部服务,使用 tryLock 超时,并把锁粒度和资源顺序设计清楚。
CAS 适合低冲突简单更新,但可能自旋消耗 CPU,有 ABA 问题,也不适合一次维护多个变量的不变式。
如果可以通过不可变数据、单线程串行化、队列或数据库唯一约束解决,就不应在业务代码里引入复杂锁。