1.Java线程池有哪些核心参数,分别有什么的作用?
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
8,
16,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(1024), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy() );
构造方法最多的是7个参数;
(1)int corePoolSize, 线程池中的核心线程数量 allowCoreThreadTimeOut;允许核心线程超时销毁; boolean prestartCoreThread(),初始化一个核心线程; int prestartAllCoreThreads(),初始化所有核心线程;
(2)int maximumPoolSize, 线程池中允许的最大线程数,当核心线程全部繁忙且任务队列存满之后,线程池会临时追加线程,直到总线程数达到maximumPoolSize这个上限;
(3)long keepAliveTime, 线程空闲超时时间,如果一个线程处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁;
(4)TimeUnit unit, keepAliveTime的时间单位 (天、小时、分、秒......);
(5)BlockingQueue<Runnable> workQueue, 任务队列,当核心线程全部繁忙时,任务存放到该任务队列中,等待被核心线程来执行;
(6)ThreadFactory threadFactory, 线程工厂,用于创建线程,一般采用默认的线程工厂即可,也可以自定义实现;
Executors.defaultThreadFactory(),
Executors.privilegedThreadFactory(),(过时)
(7)RejectedExecutionHandler handler, 拒绝策略(饱和策略),当任务太多来不及处理时,如何“拒绝”任务?
1、核心线程corePoolSize正在执行任务;
2、线程池的任务队列workQueue已满;
3、线程池中的线程数达到maximumPoolSize时;
就需要“拒绝”掉新提交过来的任务;
2.线程池有哪些拒绝策略?
JDK提供了4种内置的拒绝策略:
AbortPolicy、
CallerRunsPolicy、
DiscardOldestPolicy、
DiscardPolicy;
1、AbortPolicy(默认):丢弃任务并抛出RejectedExecutionException异常,这是默认的拒绝策略;
2、DiscardPolicy:直接丢弃任务,不抛出异常,没有任何提示;
3、DiscardOldestPolicy:丢弃任务队列中靠最前的任务,当前提交的任务不会丢弃;
4、CallerRunsPolicy: 交由任务的调用线程(提交任务的线程)来执行当前任务;
除了上面的四种拒绝策略,还可以通过实现RejectedExecutionHandler接口,实现自定义的拒绝策略;
3.说一说线程池的执行流程?
当提交一个新任务到线程池时,具体的执行流程如下:
1. 当我们提交任务,线程池会根据corePoolSize大小创建线程来执行任务;
2. 当任务的数量超过corePoolSize数量,后续的任务将会进入阻塞队列阻塞排队;
3. 当阻塞队列也满了之后,那么将会继续创建(maximumPoolSize-corePoolSize)个数量的线程来执行任务,如果任务处理完成,maximumPoolSize-corePoolSize个额外创建的线程等待 keepAliveTime之后被自动销毁;
4. 如果达到maximumPoolSize,阻塞队列还是满的状态,那么将根据不同的拒绝策略进行拒绝处理;
4.线程池核心线程数怎么设置呢?
Ncpu = cpu的核心数 ,Ucpu = cpu的使用率(在0~1之间)
W = 线程等待时间,C = 线程计算时间
举例:
8 * 100% * (1+60/40) = 20
8 * 100% * (1+80/20) = 40
任务分为CPU密集型和IO密集型
CPU密集型
线程数 = CPU核心数 + 1; 这种任务主要是消耗CPU资源, 比如像加解密、压缩、计算等一系列需要大量耗费 CPU 资源的任务; +1,比 CPU 核心数多出来的一个线程是为了防止线程偶发的缺页中断,或者其它原因导致的任务暂停而带来的影响。一旦任务暂停,CPU 就会处于空闲状态,而在这种情况下多出来的一个线程就可以充分利用 CPU 的空闲时间;
IO密集型
线程数 = CPU核心数 * 2; 这种任务会有大部分时间在进行IO操作,比如像MySQL数据库、文件读写、网络通信等任务,这类任务不会特别消耗CPU资源,但是IO操作比较耗时,会占用比较多时间; 线程在处理IO的时间段内不会占用CPU,这时就可以将CPU交出给其它线程使用,因此在IO密集型任务的应用中,可以多配置一些线程;
基本原则:
1、线程执行时间越多,就需要越少的线程;
2、线程等待时间越多,就需要越多的线程;
以上理论参考依据,实际项目中建议在本地或者测试环境进行压测多次调整线程池大小,找到相对理想的值大小;