面灵AI

去哪儿 Java 一面:线程池、虚拟线程与性能排查

轮次
一面
时间
2026-09
来源
牛客网

《面试题目》

  1. 如何介绍自己的技术背景和实习经历?
  2. 线程池有哪些重要参数,实际业务中怎样确定这些参数?
  3. 平时怎样创建线程池,Executors 提供了哪些常见实现?
  4. 使用固定大小线程池、单线程池和定时任务线程池时,各有哪些需要注意的地方?
  5. 如何理解 Java 21 的虚拟线程,它与平台线程有什么区别?
  6. 虚拟线程一定更好吗,两类线程分别适合什么场景?
  7. ThreadLocal 能否用于虚拟线程?
  8. 接口响应变慢时,你会按什么顺序排查?
  9. 怎样证明耗时出在下游接口,而不是当前服务?
  10. CPU 占用突然升高时,怎样定位到 Java 线程及具体代码?
  11. 如何检查 GC 状态,判断垃圾回收是否影响了响应时间?
  12. 平时怎样使用辅助编程工具,如何保证生成代码的模块划分合理?
  13. 实现一个 Agent,需要考虑哪些组成部分和执行流程?
  14. 如何合并以日期字符串表示的多个重叠区间?
  15. 深分页有哪些问题,如何优化?
  16. 频繁执行深分页 SQL 为什么可能推高 CPU 占用?

《参考解析》

线程池参数要和任务特征一起讲

先说明任务主要消耗 CPU,还是等待数据库、网络。再结合请求量、任务耗时、下游并发上限和可接受排队时间设置线程与队列容量,用压测检查吞吐、尾延迟和拒绝次数。固定线程池、单线程池默认使用无界队列,持续积压会消耗内存;缓存线程池则要留意线程数量增长。定时任务还要考虑任务耗时、异常和取消后的清理。

虚拟线程的收益来自减少等待占用

Java 21 的虚拟线程由 JVM 调度到平台线程上,适合大量需要等待 I/O 的任务。它不能让 CPU 密集计算凭空变快,下游数据库连接数等限制也依然存在。ThreadLocal 可以使用,但每个虚拟线程都保存大对象,会放大内存开销;不要用它为海量任务各缓存一份昂贵资源。虚拟线程通常按任务创建,限流应针对受限资源本身。相关设计说明见 OpenJDK JEP 444

从慢请求找到耗时所在

先选一条真实慢请求,用链路追踪或分段耗时区分排队、业务计算、数据库和远程调用。下游调用耗时高,还需核对连接池等待、网络建立连接和对端处理时间。CPU 高时,针对平台线程可用 top -H -p <pid> 找线程,再将线程编号转为十六进制,与线程转储中的 nid 对照。虚拟线程不与操作系统线程一一对应,需要结合支持虚拟线程的转储和性能分析工具。连续采样比单次堆栈更有意义,也要用 GC 日志和分配速率排除回收压力。

深分页为什么越翻越慢

LIMIT offset, size 往往要先读取并跳过前面的记录。偏移量越大,扫描、排序或回表的成本可能越高;具体瓶颈要看执行计划,不能把所有深分页都归结为 CPU。连续翻页可用稳定排序键加唯一键作为游标;必须随机跳页时,再评估覆盖索引、先取主键后回表等方案。

日期区间合并

先把日期解析为可比较的时间或日期值,明确区间是否包含端点。按起点排序,维护当前区间的最晚终点;下一段重叠就延长终点,否则输出当前段并开始新段。排序耗时 O(n log n)。相邻但不重叠的日期是否合并,要按题目约定处理。