去哪儿 AI 应用开发 Java 秋招:线程池扩容与 Kafka 位点
- 时间
- 2026-09
- 来源
- 牛客网
《面试题目》
- 能否做一个自我介绍?
- Java 新对象一定直接在堆的 Eden 区分配吗?
- Safepoint 与 Saferegion 有什么区别,为什么不能随时暂停应用线程?
- 提交任务增多时,线程池怎样在创建核心线程、入队和创建额外线程之间选择?
- 配置很大的有界队列后,为什么高峰期仍可能只维持核心线程数量?
- 新建的额外工作线程先执行触发扩容的任务,还是先取队列旧任务?
- CallerRunsPolicy 怎样限制提交速度,又可能怎样影响上游?
- 任务抛出 Error 后,当前工作线程还能继续复用吗?
- Kafka 消费者把消息交给线程池后,应怎样提交消费位点?
- 消费者发生再均衡时,后台尚未完成的任务怎样处理?
《参考解析》
最大线程数不是一到高峰就生效
ThreadPoolExecutor 通常先补足核心工作线程,之后优先把任务放入队列;入队失败才尝试在最大线程数内继续创建线程。大队列可能让任务长时间排队,而线程数始终没有明显增加。需要同时观察排队时间、执行耗时和拒绝次数。规则见 ThreadPoolExecutor 官方文档。
CallerRunsPolicy 会占用提交者
调用线程执行任务时,新的提交自然慢下来。但如果它本来负责网络事件或消息轮询,耗时任务也会阻塞这些职责。选拒绝策略前,需要说明谁在提交任务,以及它能承受多长时间的阻塞。线程池已经关闭时,这个策略也不会保证任务继续执行。
异常观察方式取决于怎样提交
直接 execute 的任务抛出未捕获异常,可能终止当前工作线程;submit 通常包装任务并把失败保存到 Future,工作线程未必退出。调用者不检查结果,就容易漏掉异步失败。统一记录异常时,应同时考虑直接抛出的异常和已经完成的 Future 中保存的失败。
位点只能越过已经完成的一段
同一分区中,假设较后的任务先完成,不能立即把消费位置越过仍未完成的前序记录。应跟踪已派发记录的顺序,只有前面所有相关任务都完成,才推进可提交边界。Kafka 位点不保证每个整数都有可见记录,因此不能简单要求逐个整数都出现完成标记。
发生分区移交时,停止继续派发受影响分区的任务,处理有限时间内可完成的工作,并约束迟到结果。未确认完成的记录可能被再次处理,所以数据库写入和外部副作用仍要幂等。普通 KafkaConsumer 的操作也应保持在线程安全边界内。