线程安全即共享数据多线程读写结果符合单线程预期,需同时满足原子性(如atomicinteger)、可见性(volatile)和有序性(volatile/synchronized);死锁预防须统一加锁顺序、trylock超时、减小锁粒度;线程池应选有界队列、自定义拒绝策略;volatile不保证原子性,synchronized则三者兼具。

高效回答Java多线程高并发面试题,关键不是堆砌术语,而是用清晰逻辑把“问题本质—常见错误—正确解法—落地细节”串成一条线。面试官想听的不是定义复述,而是你是否真理解场景、权衡取舍和边界条件。
紧扣三要素讲清线程安全
被问“什么是线程安全”或“怎么保证”,别只说“加synchronized”。先点明并发编程的三个底层约束:
- 原子性:比如i++看似一行,实为读-改-写三步,中间可能被其他线程打断;用AtomicInteger或synchronized包住整个操作才能保证不可分割。
- 可见性:一个线程改了变量值,另一个线程不一定马上看到——volatile能解决这个,但不能替代锁来保原子性。
- 有序性:JVM和CPU可能重排序指令,volatile和锁都能禁止重排,但普通变量不保证。
一句话收尾:“线程安全,就是让共享数据在多线程读写时,结果符合单线程执行的预期。”
死锁回答要带预防动作,不只讲现象
说到死锁,光说“AB互相等对方的锁”太浅。直接给出可落地的预防手段:
- 统一加锁顺序:比如操作账户A和B,所有线程都按ID小的先锁、大的后锁。
- 用tryLock()设超时:ReentrantLock.tryLock(1, TimeUnit.SECONDS),拿不到就放弃或重试,避免无限等待。
- 减少锁粒度:把大锁拆成多个小锁,或改用无锁结构(如ConcurrentHashMap代替HashTable)。
补充一句:“线上我们还会用jstack定期采样线程栈,配合监控告警,第一时间发现潜在死锁苗头。”
线程池问题必须关联业务场景
问到ThreadPoolExecutor参数,别光背corePoolSize、maxPoolSize……要说明它们怎么影响真实系统:
- corePoolSize不是“最小线程数”,而是“长期驻留线程数”——它决定了常驻开销和冷启动延迟。
- workQueue选有界队列(如ArrayBlockingQueue),避免任务积压OOM;默认的LinkedBlockingQueue无界,是很多生产事故的根源。
- 拒绝策略选CallerRunsPolicy时要注意:提交线程自己执行任务,可能拖慢上游接口响应,适合后台异步任务;秒杀场景更倾向DiscardOldestPolicy+告警。
顺手提一句:“我们用的是自定义ThreadPoolExecutor,而不是Executors.newFixedThreadPool(),就是为了控死队列容量和拒绝行为。”
volatile和synchronized别混为一谈
高频误区:以为volatile能替代synchronized。回答时明确划清边界:
- volatile只保可见性+有序性,不保原子性——i++仍需同步或Atomic类。
- synchronized既保原子性,也保可见性和有序性,且能阻塞线程;但它是重量级锁,高竞争下性能不如Lock或CAS。
- 适用场景举例:状态标志位(running = false)用volatile足够;账户余额扣减必须用synchronized或AtomicLong.addAndGet(-amount)。
这样答,面试官立刻知道你踩过坑、分得清轻重。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











