线程池实战经验
                           
天天向上
发布: 2025-04-26 19:42:38

原创
683 人浏览过

本章节整理一版线程池实战经验,这部分非常贴近面试官爱考现场工作爱用的内容,供大家参考!


1. 如何自定义 ThreadFactory?要注意什么?

标准回答:

  • 自定义 ThreadFactory,通常是为了:
  • 给线程命名,便于日志排查、监控。
  • 设置是否是守护线程(Daemon)。
  • 设置线程优先级,必要时使用。

示例代码:

ThreadFactory customThreadFactory = runnable -> {
    Thread thread = new Thread(runnable);
    thread.setName("my-pool-thread-" + thread.getId());
    thread.setDaemon(false); // 生产一般设置为非守护线程
    return thread;
};

注意事项:

  • 保证线程创建逻辑简单、稳定,不能有副作用。
  • 命名统一规范,便于排查问题。
  • 避免线程创建过多导致资源耗尽。

2. 如何避免线程池中的内存泄漏问题?

标准回答:

  • 根本原因:提交到线程池的任务对象持有大对象引用,导致任务即使完成也不能被 GC。
  • 常见场景:匿名内部类、Lambda 捕获外部大对象。

解决方法:

  • 不要在任务中不小心引用外部大对象
  • 用静态内部类代替非静态内部类,减少对外部引用的隐式持有。
  • 定时清理过期任务,或者使用合理的生命周期管理。

举个坑例子:

BigObject bigObject = new BigObject();
executor.submit(() -> {
    // 错误!这里 Lambda 捕获了 bigObject
    bigObject.doSomething();
});

应该改成:

executor.submit(new BigObjectTask(bigObject));

3. 不同业务场景,线程池参数应该怎么配?

标准回答:

业务类型corePoolSize / maximumPoolSize队列类型特点
CPU密集型(计算、加密)核心线程数 = CPU核数(n)或 n+1SynchronousQueue 或 ArrayBlockingQueue保证 CPU 不切换上下文
IO密集型(数据库、文件)核心线程数 = 2n ~ 4nLinkedBlockingQueue避免 IO 阻塞导致 CPU 空闲
高并发低延迟系统核心线程数 = 2n,最大线程数很大SynchronousQueue响应快,牺牲一些资源消耗
普通后台处理核心小(如2-4),最大适中(如8-16)LinkedBlockingQueue保持系统稳定性优先

总结一句话

“CPU 密集限数量,IO 密集放宽,延迟敏感快扩容。”


4. 如何合理设置 keepAliveTime?

标准回答:

  • 如果线程扩展主要用于短时流量高峰,可以设置较小的 keepAliveTime,比如 30 秒60 秒
  • 如果希望线程存活时间长一点(减少频繁销毁带来的开销),可以设置大一点,比如 5 分钟
  • 对核心线程调用 allowCoreThreadTimeOut(true) 时,要特别小心,防止线程池收光,导致线程池频繁创建新线程 ➔ 反而更慢。

经验值

一般 keepAliveTime = 1分钟以内就够用,特例除外。


5. 线程池调优时有哪些监控指标必须盯住?

标准回答:

关键指标:

指标说明重点
activeCount正在运行的线程数过高说明线程忙不过来
queueSize队列中等待的任务数持续上升说明处理不过来
completedTaskCount已完成任务数反映处理能力
largestPoolSize曾经达到的最大线程数评估配置是否合理
rejectionCount被拒绝的任务数量监控业务异常风险

结论

监控 ➔ 调整参数 ➔ 防止排队堆积 ➔ 保证系统稳定。


6. 大量短任务适合怎样的线程池设计?

标准回答:

  • 如果任务很短(比如几十毫秒内完成),应该:
  • 设置较大的核心线程数,避免频繁提交、排队。
  • 使用较小或者同步型的队列(如 SynchronousQueue)。
  • 保证 CPU 尽量保持忙碌,而不是频繁切换上下文。

选型推荐

ThreadPoolExecutor + SynchronousQueue,配合合理的最大线程数,最适合处理高频短任务。


7. 如何防止线程池中的任务“卡死”?

标准回答:

  • 给任务设置超时,比如通过 Future.get(timeout, TimeUnit) 来限时拿结果。
  • 在线程池外控制每个任务的最大执行时间。
  • 提交可取消的任务(比如用 Callable 而不是 Runnable)。

例子:

Future<?> future = executor.submit(task);
try {
    future.get(5, TimeUnit.SECONDS); // 最多等5秒
} catch (TimeoutException e) {
    future.cancel(true); // 超时后取消任务
}

结论

不信任任何任务能快速完成,要自己兜底。


更多详细内容请关注其他相关文章!

发表回复 0

Your email address will not be published. Required fields are marked *