线程池各参数含义
先来看看源代码,线程池创建都需要传哪些参数,上代码
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
从代码中我们可以看到创建一个线程池是需要7个参数,先给下官方的解释大概了解下:
- corePoolSize the number of threads to keep in the pool, even if they are idle,百度翻译:池中要保留的线程数,即使它们处于空闲状态;(也就是我们说的核心线程数)
- maximumPoolSize the maximum number of threads to allow in the pool,百度翻译:池中允许的最大线程数;
- keepAliveTime when the number of threads is greater than the core, this is the maximum time that excess idle threads will wait for new tasks before terminating,翻译太长:大致意思就是,当 线程数 > corePoolSize时, 多余的这部分线程空闲一定时间后,开始终止线程;
- unit the time unit for the {@code keepAliveTime} argument,这个就很简单了,keepAliveTime的单位;
- workQueue the queue to use for holding tasks before they are executed. 也没什么好说的,等待队列;
- threadFactory the factory to use when the executor creates a new thread. 看的factory大致也能猜到是一个工厂类,用来创建线程;
- handler the handler to use when execution is blocked because the thread bounds and queue capacities are reached. 拒绝策略,线程池执行不过来的时候的策略,可自定义,jdk提供了四种拒绝策略。
线程池的工作方式
知道了各参数的含义,可能也不是太明白什么意思,那么我用一件生活中常见的场景来说明下,可以更好的理解各参数以及他们在线程池运行中扮演的角色;
-
银行办理业务场景
大家都去过银行吧,而且对于苦逼的上班族来说只有周末才有时间去银行办理业务;周六了,张三带着一个礼拜的加班费来银行办理存款,发现五个窗口只有两个窗口正常办理,其他三个周末不开;来晚了,大厅座椅已经有人在排队,张三拿了号也坐了下来;慢慢人越来越多,等候区已经基本坐满了;值班经理没想到人这么多,就赶紧给同事王五、王六、王七打电话让来加班;几分钟后5个窗口都开通了,可是人还是一直增加,等候区已经爆满,这时候经理安排大厅保安站门口,阻挡要进来的办理业务人员并高诉他们今天已经没号了,不要再进来办理业务;等了一段时间张三终于办理完了业务,已经到了下午,办理业务的人也减少了,5个窗口开始慢慢有空闲的现象,这时候经理对王五六七说如果过10分钟你们窗口还是空闲,你们就可以下班了;场景大致描述完了,不知道大家有没有悟到什么。 -
银行场景和线程池相似处
开始的两个窗口其实就是corePoolSize;五个窗口就是maximumPoolSize包含corePoolSize;银行等候区对应的就是workQueue;经理对应的就是threadFactory,他可以安排王五六七来加班,也就是开启新线程;保安站门口阻挡人员就等同于拒绝策略,当然拒绝策略不是真正的拒绝,我们可以自定义,有时候可以加个补偿处理策略;经理说过10分钟没有办理业务的,你们就可以下班,对应的就是线程池的keepAliveTime和unit参数。
创建线程池方式
Excutor
- 创建线程池方式
ExecutorService threadPool = new ThreadPoolExecutor(1,2,1000。。。);
Excutors
类后面带s的都是辅助工具类,例如String Strings,Array Arrays;
- 应用Excutors辅助类创建线程池
- 创建指定size数 :
ExecutorService threadPool = Excutors.newFixedThreadPool(5);
- 创建单线程线程池:
ExecutorService threadPool = Excutors.newSingleThreadExecutor();
- 创建一个可缓存线程池:
ExecutorService threadPool = Excutors.newCachedThreadPool();
题外话:为什么大厂不允许用Excutors来创建线程池?其实看Excutors底层代码可以看出,底层还是用了new ThreadPoolExecutor()来创建,但是maxSize默认最大会导致栈溢出,queue默认无界,会导致oom,都是不可取的。
线程池数量选取
一般我们业务分为CPU密集型和IO密集型,两种业务在线程池数量上选取是有差异的。
-
cpu密集型
线程数 = cpu核数 + 1 -
IO密集型
线程数 = cpu核数 * 2 + 1
线程数 = cpu核数 / (1- 0.8~0.9)