文章目录
- 一、缓存队列 LinkedBlockingQueue 没有设置固定容量大小
-
- 1.1、Executors.newFixedThreadPool()
- 1.2、Executors.newSingleThreadExecutor()
- 总结:
- 二 、最大线程数量是 Integer.MAX_VALUE
-
- 2.1、Executors.newCachedThreadPool()
- 2.2、Executors.newScheduledThreadPool()
- 总结:
- 三、拒绝策略不能自定义(这个不是重点)
- 四、创建线程 或 线程池时请指定有意义的线程名称,方便出错时回溯(这个不是重点)
阿里发布的 Java开发手册中强制线程池不允许使用 Executors
去创建,而是通过 ThreadPoolExecutor
的方式。这是为什么?
有4个原因,前2个是主要原因。具体如下:
一、缓存队列 LinkedBlockingQueue 没有设置固定容量大小
1.1、Executors.newFixedThreadPool()
创建固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
ThreadPoolExecutor 部分参数:
- corePoolSize :线程池中核心线程数的最大值。此处为 nThreads个。
- maximumPoolSize :线程池中能拥有最多线程数 。此处为 nThreads 个。
- LinkedBlockingQueue 用于缓存任务的阻塞队列 。 此处没有设置容量大小,默认是 Integer.MAX_VALUE,可以认为是无界的。
问题分析:
从源码中可以看出, 虽然表面上 newFixedThreadPool()
中定义了 核心线程数 和 最大线程数 都是固定 nThreads 个,但是当 线程数量超过 nThreads 时,多余的线程会保存到 LinkedBlockingQueue 中,而 LinkedBlockingQueue 没是无界的,导致其无限增大,最终内存撑爆。
1.2、Executors.newSingleThreadExecutor()
创建单个线程池 ,线程池中只有一个线程。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
创建单个线程池 ,线程池中只有一个线程。
优点: 创建一个单线程的线程池,保证线程的顺序执行 ;
缺点: 与 newFixedThreadPool() 相同。
总结:
newFixedThreadPool()、newSingleThreadExecutor() 底层代码 中 LinkedBlockingQueue 没有设置容量大小,默认是 Integer.MAX_VALUE, 可以认为是无界的。线程池中 多余的线程会被缓存到 LinkedBlockingQueue中,最终内存撑爆。
二 、最大线程数量是 Integer.MAX_VALUE
2.1、Executors.newCachedThreadPool()
缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
ThreadPoolExecutor 部分参数:
- corePoolSize :线程池中核心线程数的最大值。此处为 0 个。
- maximumPoolSize :线程池中能拥有最多线程数 。此处为 Integer.MAX_VALUE 。可以认为是无限大 。
优点: 很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建 ;
缺点: 从源码中可以看出,SynchronousQueue() 只能存一个队列,可以认为所有 放到 newCachedThreadPool() 中的线程,不会缓存到队列中,而是直接运行的, 由于最大线程数是 Integer.MAX_VALUE ,这个数量级可以认为是无限大了, 随着执行线程数量的增多 和 线程没有及时结束,最终会将内存撑爆。
2.2、Executors.newScheduledThreadPool()
创建固定大小的线程,可以延迟或定时的执行任务
public static ScheduledExecutorService newScheduledThreadPool(
int corePoolSize, ThreadFactory threadFactory) {
return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}
// ScheduledThreadPoolExecutor 类的源码:
public ScheduledThreadPoolExecutor(int corePoolSize,
ThreadFactory threadFactory) {
super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
new DelayedWorkQueue(), threadFactory);
}
优点: 创建一个固定大小线程池,可以定时或周期性的执行任务 ;
缺点: 与 newCachedThreadPool() 相同。
总结:
newCachedThreadPool()、newScheduledThreadPool() 的 底层代码 中 的 最大线程数(maximumPoolSize) 是 Integer.MAX_VALUE,可以认为是无限大,如果线程池中,执行中的线程没有及时结束,并且不断地有线程加入并执行,最终会将内存撑爆。
三、拒绝策略不能自定义(这个不是重点)
它们统一缺点:不支持自定义拒绝策略。
Executors
底层其实是使用的 ThreadPoolExecutor
的方式 创建的,但是使用的是 ThreadPoolExecutor
的默认策略,即 AbortPolicy
。
//默认策略
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
//构造函数
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
四、创建线程 或 线程池时请指定有意义的线程名称,方便出错时回溯(这个不是重点)
这个第四条原因参考了: https://blog.csdn.net/w605283073/article/details/80259493
欢迎关注微信公众号:shoshana
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· 没有源码,如何修改代码逻辑?
· 一个奇形怪状的面试题:Bean中的CHM要不要加volatile?
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
2018-11-24 电商-大中台+小前端
2018-11-24 新零售的舞台上,创业者如何与大象共舞
2018-11-24 无人零售的黑科技:RFID技术