场景实战:多线程与并发问题调优
❓ 面试实战:你们系统中哪里用到了多线程?怎么用的?
频率:🔥🔥🔥🔥🔥
💡 一句话总结(先抛结论): 我们在“耗时任务异步化”和“多接口并行聚合查询”这两个高频场景中大量使用了多线程(线程池),将接口响应时间从秒级压缩到了百毫秒级。
📝 详细业务场景落地(面试高分答题模板):
场景一:耗时任务异步化(提升主流程响应速度)
- 业务痛点:用户注册/下单成功后,系统需要:1. 写入数据库;2. 发送成功短信;3. 发送新用户优惠券;4. 记录操作日志。如果串行执行,整个接口可能要耗时 2~3 秒,用户体验极差(一直在转圈圈)。
- 实战做法:
- 核心主流程(写库)依然同步执行,耗时 50ms。
- 非核心流程(发短信、发券、写日志),直接封装成
Runnable或Callable任务,扔给自定义的业务异步线程池(如AsyncEventThreadPool)去后台默默执行。 - 主线程不用等它们执行完,直接返回“注册成功”。接口耗时瞬间降到 50ms。
- 坑点防范:如果线程池满了或者服务器突然宕机,这些扔在内存队列里的异步任务会丢失。因此对于极度重要的非核心任务(如发券),我们最终会改用 MQ(消息队列) 来替代本地多线程,保证消息持久化不丢失。
场景二:多接口并行聚合查询(CountDownLatch/CompletableFuture)
- 业务痛点:在“App 首页”或者“商品详情页”,前端调用我们一个接口,但后端需要去组装大量数据。比如查商品信息(200ms)、查店铺信息(100ms)、查商品评价(150ms)、查用户浏览历史(100ms)。串行查完总共需要
200+100+150+100 = 550ms。 - 实战做法:
- 我们使用了 JDK 8 的
CompletableFuture进行多线程编排。 - 将这 4 个查询任务分别提交给线程池并行执行。
- 使用
CompletableFuture.allOf(f1, f2, f3, f4).join()阻塞等待所有任务全部完成。 - 最终耗时:取决于最慢的那个任务(即 200ms),性能直接提升了一倍多!
- 我们使用了 JDK 8 的
❓ 面试实战:线上如果出现了 CPU 100% 或者线程死锁,你怎么排查定位?【高级】
频率:🔥🔥🔥🔥
💡 一句话总结(先抛结论):
- 排查 CPU 100%:使用
top命令找到最耗 CPU 的进程 PID -> 用top -Hp找到该进程下最耗 CPU 的线程 ID -> 把线程 ID 转成16进制 -> 用jstack导出线程快照,搜索该 16 进制定位到具体的 Java 代码行数(通常是写了死循环或者频繁 Full GC)。 - 排查死锁:直接使用
jstack或者 JDK 自带的jconsole / jvisualvm,工具会自动帮你检测并在日志最后打印出Found one Java-level deadlock。
📝 详细排查命令实操(死记硬背,面试必考):
第一题:排查 CPU 飙高
- 找出最耗 CPU 的进程 ID(假设是 10086):bash
top -c - 查看这个进程内部,哪个线程最耗 CPU(假设是 10090):bash
top -H -p 10086 - 将十进制的线程 ID (10090) 转换成十六进制(假设结果是
276a):bashprintf "%x\n" 10090 - 绝杀:使用
jstack打印堆栈日志,并grep搜索这个十六进制的线程号:bash结果:终端会直接打印出这个线程正在执行的堆栈信息,你一眼就能看出是哪个类、哪一行的jstack 10086 | grep -A 20 "276a"while(true)代码导致了 CPU 狂飙,或者看到是VM Thread正在疯狂做垃圾回收。
第二题:排查死锁(Deadlock)
- 死锁产生的四个必要条件:互斥、请求与保持、不剥夺、循环等待。我们写代码最容易犯的就是循环等待(A 拿了锁 1 等锁 2,B 拿了锁 2 等锁 1)。
- 排查方法:直接执行
jstack 进程号,滑到日志的最底部。JVM 非常智能,它会明确告诉你:text根据日志提示的代码行数,去修改加锁的顺序即可解决。Found one Java-level deadlock: ============================= "Thread-A": waiting to lock monitor 0x0000... (object 2), which is held by "Thread-B" "Thread-B": waiting to lock monitor 0x0000... (object 1), which is held by "Thread-A"
🌟 面试加分项(现代化工具): "当然,在现代企业级开发中,我们如果再去敲那一堆 Linux 命令行就有点过时了。我们线上通常会部署 Alibaba Arthas(阿尔萨斯) 诊断工具。 排查 CPU 100% 只需要敲一行命令:thread -n 3,它就会直接打印出当前最忙的 3 个线程的堆栈和耗费 CPU 的具体行数。 排查死锁也只需要一行命令:thread -b,瞬间揪出造成阻塞的罪魁祸首。"