一位大厂面试官的灵魂发问:Executor 和 Executors 有什么区别?



你有没有被"线程池"打过脸?

先说个故事吧。

上个月我去面一家知名大厂,二面官看起来特别和蔼,就像楼下便利店大叔那种感觉。我本以为这一面轻轻松松,没想到......

面试官:你了解 Java 的线程池吗?

我:嗯嗯,知道的,用 Executors 创建线程池嘛,挺方便的,比如 Executors.newFixedThreadPool(10) 这种。

面试官点点头,露出诡异的微笑:那你知道为啥不推荐用 Executors 吗?

我:啊......?不是挺好用的吗?这......

空气突然安静。

我努力回忆当年看的 Java 并发编程艺术,结果脑袋一片空白,支支吾吾地说了几句含糊其辞的话,眼看着面试官的眉头越皱越紧。

面试官叹了口气,说:"没事,这个问题很多人都答不上来。"

当时我就一个念头:

线程池啊线程池,你这是给我上了一课!

于是,面试回来我连夜查文档、翻源码、画图、写博客,发誓下次再被问到这个问题,我一定把它讲得明明白白,像讲故事一样讲给别人听!

今天,就让我把这个故事讲完,也希望你不要像当初的我一样,被这道"Executors 和 ThreadPoolExecutor 的区别"给绊倒。

Executors 是糖,ThreadPoolExecutor 是本体

1. Executors 是啥?

Executors 是 Java 提供的一个工具类 ,里面提供了一堆静态方法,可以帮你方便快捷地创建各种线程池。

比如这些:

这些方法看起来是不是很优雅?的确,好看,好用,好记,用它可以一行代码创建线程池,谁不爱呢?

但,这也埋下了一个雷......

2. ThreadPoolExecutor 才是真正的线程池实现

ThreadPoolExecutor 是 java.util.concurrent 包下的核心类,它才是线程池的真正实现者。Executors 内部就是调用了它来构造不同类型的线程池。

我们来看一个手动创建线程池的例子:

这个构造方法虽然啰嗦,但每一个参数你都可以控制! 这就是和 Executors 最大的区别!

用 Executors 是"糖衣炮弹",吃起来爽,背后全是坑。

为什么说 Executors 不推荐用了?

现在你可能会问,Executors 不是封装好的吗?为啥还说它是"坑"?

原因就三个字:

不可控!

1. Executors.newFixedThreadPool()

它底层用的是:

重点来了:它用了一个无限长的阻塞队列LinkedBlockingQueue!

这意味着啥?

  • 如果你提交了成千上万个任务,线程池不扩容,任务全都堆在队列里,可能会导致内存暴涨、OOM(Out Of Memory)

简直是温水煮青蛙!

2. Executors.newCachedThreadPool()

它底层是这样的:

看出来了吗?

  • 核心线程数是 0
  • 最大线程数是 Integer.MAX_VALUE(20 多亿!)
  • 队列是 SynchronousQueue(不会缓存任务)

这意味着:

  • 你每提交一个任务,它就会尝试新建线程,直到撑爆你的 CPU 或内存!

所以,不加限制地用 CachedThreadPool,风险极大。

3. Executors.newSingleThreadExecutor()

同样的,底层也用了无限长的 LinkedBlockingQueue。

一旦处理不过来,任务又堆积如山。

4、总结一下

所以,阿里巴巴的《Java开发手册》中明确写到:

  • 线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样规避资源耗尽的风险。

正确姿势:手动创建线程池

那我们应该怎么做?

老老实实用 ThreadPoolExecutor,自己掌控线程数和队列容量

比如一个推荐的线程池创建方式:

这样,你能控制最大线程数量、队列容量、线程回收时间、拒绝策略,一切尽在掌握中。

面试官到底想听你说什么?

你以为面试官问你"Executors 和 ThreadPoolExecutor 有什么区别",是想听你背 API 吗?

No!他想听的是你是否理解背后的风险,是否有工程实践意识。

如果你能这样回答他:

"虽然 Executors 提供了便捷的方法,但由于它默认采用无限队列或无限线程,容易引发内存溢出或系统负载问题,因此我们推荐使用 ThreadPoolExecutor 并手动设置参数,避免系统崩溃。"

你说完这段,面试官多半会点头微笑,可能还会多给你一些面试分。

常见线程池参数详解(收藏版)

再来一个彩蛋:线程池调优建议

  • 对于高并发但执行时间短的任务,使用 FixedThreadPool 配有限长队列;
  • 对于执行时间长的任务,应控制线程数,避免占满 CPU;
  • 对于I/O密集型任务,可以设置核心线程略高于 CPU 核心数;
  • 别忘了设置合理的拒绝策略,比如 CallerRunsPolicy 能在任务过载时回退到调用者线程执行。

最后,小米的面试反杀记

就在上周,我又去面了一家大厂。

这次,面试官又问:

"你说说 Executors 和 ThreadPoolExecutor 的区别。"

我一笑,娓娓道来,从源码到风险,再到推荐方案,说得对方频频点头。

最终,HR 面说:

"你这回答比我们内部文档都清楚。"

我终于体会到:

被一道题打败,不是终点;弄懂它,讲出来,就是你进阶的开始。

END

我是小米,感谢你的阅读。

如果你喜欢这种"讲故事 + 技术干货"的文章风格,欢迎点个 ,或者分享给你身边也在准备面试的小伙伴~

下期我打算写:Future、CompletableFuture 和线程池的终极关系图解,你感兴趣吗?

我是小米,一个喜欢分享技术的31岁程序员。如果你喜欢我的文章,欢迎关注我的微信公众号"软件求生",获取更多技术干货!

相关推荐
CodeSheep13 分钟前
Stack Overflow,轰然倒下了!
前端·后端·程序员
GoGeekBaird22 分钟前
GoHumanLoopHub开源上线,开启Agent人际协作新方式
人工智能·后端·github
水痕0140 分钟前
gin结合minio来做文件存储
java·eureka·gin
Victor3561 小时前
Redis(8)如何安装Redis?
后端
寒士obj1 小时前
Spring事物
java·spring
Victor3561 小时前
Redis(9)如何启动和停止Redis服务?
后端
柯南二号2 小时前
【Java后端】Spring Boot 集成 MyBatis-Plus 全攻略
java·spring boot·mybatis
程序员爱钓鱼3 小时前
Go语言实战案例-创建模型并自动迁移
后端·google·go
javachen__3 小时前
SpringBoot整合P6Spy实现全链路SQL监控
spring boot·后端·sql