去哪儿 Java 一面:线程池、虚拟线程与性能排查
- 轮次
- 一面
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 如何介绍自己的技术背景和实习经历?
- 线程池有哪些重要参数,实际业务中怎样确定这些参数?
- 平时怎样创建线程池,Executors 提供了哪些常见实现?
- 使用固定大小线程池、单线程池和定时任务线程池时,各有哪些需要注意的地方?
- 如何理解 Java 21 的虚拟线程,它与平台线程有什么区别?
- 虚拟线程一定更好吗,两类线程分别适合什么场景?
- ThreadLocal 能否用于虚拟线程?
- 接口响应变慢时,你会按什么顺序排查?
- 怎样证明耗时出在下游接口,而不是当前服务?
- CPU 占用突然升高时,怎样定位到 Java 线程及具体代码?
- 如何检查 GC 状态,判断垃圾回收是否影响了响应时间?
- 平时怎样使用辅助编程工具,如何保证生成代码的模块划分合理?
- 实现一个 Agent,需要考虑哪些组成部分和执行流程?
- 如何合并以日期字符串表示的多个重叠区间?
- 深分页有哪些问题,如何优化?
- 频繁执行深分页 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)。相邻但不重叠的日期是否合并,要按题目约定处理。