Executors类创建
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
这种方式是jdk自带的创建线程池的方式,阿里的规范里是不推荐这样创建的
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors各个方法的弊端:
1)newFixedThreadPool和newSingleThreadExecutor:
主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至OOM。
2)newCachedThreadPool和newScheduledThreadPool:
主要问题是线程数最大数是Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至OOM。
-
Executors类创建源码(底层都是调用new ThreadPoolExecutor)
image.png
ThreadPoolExecutor创建
- 构建函数(7参数)
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
name | 含义 |
---|---|
corePoolSize | 核心线程数 |
maximumPoolSize | 最大线程数 |
keepAliveTime | 线程池中超过 corePoolSize 数目的空闲线程最大存活时间 |
unit | 存活时间单位 |
workQueue | 阻塞任务队列 |
threadFactory | 新建线程工厂 |
handler | 拒绝策略,当请求的线程数超过maximumPoolSize+workQueue时,交过handler处理 |
- 流程
- 当请求的线程数小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程
- 当请求的线程数大于corePoolSize时,会把新提交的任务存入到workQueue里面排队等待。
- 当请求数量到达corePoolSize+workQueue的长度时,会把此时的线程池扩容到maximumPoolSize的数量
- 当请求超过workQueue的长度+maximumPoolSize的长度时,会让拒绝策略RejectedExecutionHandler 来处理这些请求
- 以银行办办业务为例
corePoolSize相当于现在开放的窗口(假定为2),
maximumPoolSize银行能开放的最大窗口数量(假定为5),
workQueue 候客区(假定为3),
比如现在只有一个顾客来的时候,直接就给他办理业务此时size=1,
当有三个顾客来的时候,因为此时业务窗口(corePoolSize)只有2,所以有一个人得进入候客区(workQueue)排队,
当有6个顾客来时,此时需要处理的数量已经超过了(coresize+workQueue.size),此时银行需要临时开放其他窗口来给顾客办理业务,最大可以将所有窗口都开放,及开放窗口数量最大为maximumPoolSize
如果此时来了11个顾客,此时请求的数量已经超过了最大开放的窗口(maximumPoolSize)+候客区(workQueue),网点已经不能再容纳了,此时就把其他来办理业务的人交给RejectedExecutionHandler来处理拒绝掉
当人越来越少时,新开放的窗口已经空闲了,如果此时空闲时间超过keepAliveTime,就让这个窗口的营业员下班,关闭这个窗口,直到最后只剩下最开始的两个窗口(corePoolSize)
四大拒绝策略(等待队列已经满了,再也塞不下新的任务,同时线程池中的线程数达到了最大线程数,无法继续为新任务服务)
- AbortPolicy:处理程序遭到拒绝将抛出运行时 RejectedExecutionException
- CallerRunsPolicy:把任务回推给线程提交方让他自己执行(例如回推给main线程),此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。
- DiscardPolicy:不能执行的任务将被删除
- DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)
线程池配置参考
- CPU 密集型
CPU 密集的意思是该任务需要大量的运算,而没有阻塞,CPU 一直全速运行。
CPU 密集型任务尽可能的少的线程数量,一般为 CPU 核数 + 1 个线程的线程池。 - IO 密集型
由于 IO 密集型任务线程并不是一直在执行任务,可以多分配一点线程数,如 CPU * 2 。
也可以使用公式:CPU 核数 / (1 - 阻塞系数);其中阻塞系数在 0.8 ~ 0.9 之间。。
网友评论