ThreadPool线程池
线程资源通过线程池提供
线程池可以减少创建、销毁线程的开销,解决资源不足问题。
如果手动创建线程,容易造成系统存在大量同类线程而导致内存耗尽、过度切换问题。
不使用Executors
Executors.newFixedThreadPool()
固定大小线程池
Executors.newSingleThreadExecutor()
单线程池
Executors.newCachedThreadPool()
动态线程池
Executors底层仍然使用
new
来创建线程,容易造成OOM。
自己调用ThreadPoolExecutor
推荐的方法使,直接自己调用new ThreadPoolExecutor
来创建线程池,设置自己的参数
public ThreadPoolExecutor( |
corePoolSize
核心线程数量。
maximumPoolSize
最大线程数量(核心 + 临时)。只有核心线程满了,而且阻塞队列也满了,才会创建临时线程。
keepAliveTime
临时线程的空闲存活时间。
threadFactory
线程工厂,即以什么方式创建线程。
handler
核心线程、阻塞队列、临时线程都满了,触发拒绝策略。
Executors
的问题在于,它指定的阻塞队列大小是Integer.MAX_VALUE
,会导致内存溢出;而Executors.newCachedThreadPool()
更是指定了maximumPoolSize
为Integer.MAX_VALUE
。
拒绝策略,如何保证线程不丢
使用直接拒绝AbortPolicy
策略,线程会丢失。此时可以使用
CallerRunsPolicy
直接在主线程同步执行(可能会阻塞主线程)DiscardOldestPolicy
将队列头部删除,新线程尾部入队(保证新任务优先级)DiscardPolicy
可以自己拓展,例如将任务放到redis、rocketmq中
ThreadPool工作流程
当前线程数没有达到corePoolSize
之前,每个新任务都会触发创建新线程;
达到以后,才会放到阻塞队列里,等待线程任务执行完成,分发任务给核心线程。
阻塞队列满了,才会创建临时线程执行任务。
ThreadPool如何初始化?
供参考的经验值:
- CPU密集任务(如数据统计、排序):
核心线程 = 最大线程 = 核心数 + 1
减少上下文切换开销 - IO密集任务:
核心线程 = 核心数 * 2;最大线程 = 核心数 * 4