配置线程池参数
代码演示
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize,//最大核心线程数
long keepAliveTime,//超时时间
TimeUnit unit,//超时时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程池工厂
RejectedExecutionHandler handler)//拒绝策略
图形演示
如上图所示, 窗口1、窗口2就是我们的核心线程数corePoolSize
, 窗口3、窗口4、窗口5就是我们的可扩容的最大核心线程数maximumPoolSize
,候车1、候车2、候车3表示我们的阻塞队列
下面进入代码测试,自己配置线程池参数:
public class MyThreadDemo {
public static void main(String[] args) {
ExecutorService threadPool = new ThreadPoolExecutor(
2,//corePoolSize
3,//maximumPoolSize
3,//keepAliveTime
TimeUnit.SECONDS,//TimeUnit unit
new LinkedBlockingDeque(2),//BlockingQueue<Runnable> workQueue
Executors.defaultThreadFactory(),//ThreadFactory threadFactory
new ThreadPoolExecutor.AbortPolicy() //RejectedExecutionHandler handler
);
try {
for (int i = 0; i < 5; i++) {
//之前都是手动自己new线程,效率不高 new Thread(()->{sout(); }).start();
threadPool.execute(()->{
System.out.println(Thread.currentThread().getName()+"开始执行");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool.shutdown();
}
}
}
corePoolSize
就是一开始当你提交任务的时候就先帮你创建好线程的个数
LinkedBlockingDeque
阻塞队列就是当你提交的任务数大于corePoolSize
个数,会临时帮你放在这里,就相当于上面的候车区一样等待执行,但是此时线程个数还是在corePoolSize
配置的范围之内,不会开启第三个线程,
如果我把上面的for循环改成 i<2 ,结果如下所示
pool-1-thread-2开始执行
pool-1-thread-1开始执行
这个时候你提交的任务数其实是<=corePoolSize
,核心线程个数是完全够用的,不用开辟第三个线程执行,所以只会存在thread-1
,thread-2
这两个线程在执行任务
如果我把上面的for循环改成 i<4 ,结果如下所示
pool-1-thread-2开始执行
pool-1-thread-1开始执行
你会发现结果还是一样的,只会开启两个线程执行任务,虽然你提交的任务数大于了corePoolSize
个数, 但是因为这里的阻塞队列
将你的第三个、第三个提交的任务给缓存起来了,等待两个线程中的其中一个执行完就会开始从阻塞队列中获取任务进行执行,所以这里也不用开启第三个线程执行
那什么时候回开启第3个线程执行呢?
当我们的这个提交的任务数>corePoolSize
个数+LinkedBlockingDeque
个数时,也就是corePoolSize
满了,LinkedBlockingDeque
也满了,就会开启第3个线程执行,这里我们把for循环里的值改成5,这个时候就会开启我们的maximumPoolSize
配置的线程,因阻塞队列最大就只能给你缓存2个任务数,然后corePoolSize
个人也只最多处理2个任务请求,所以还剩下一个任务得扩张资源,所以得把线程3给开启执行任务,所以这里一共就会有3个线程开始执行任务,结果如下所示:
pool-1-thread-1开始执行
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
pool-1-thread-3开始执行
pool-1-thread-2开始执行
但是这里你要注意,如果你把for循环里的值设置超过了5,因为这里我们最多就只能开=maximumPoolSize+LinkedBlockingDequed
个,也就是5个,这个时候因为最多撑死就只能够同时执行5个任务,所以这里会抛出拒绝策略,拒绝策略默认是直接抛出异常,我这里在for循环设置成6,结果如下图所示:
pool-1-thread-2开始执行
pool-1-thread-2开始执行
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
java.util.concurrent.RejectedExecutionException: Task com.cn.MyThreadDemo$$Lambda$1/1023892928@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 3]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.cn.MyThreadDemo.main(MyThreadDemo.java:21)
上述代码方便演示,所以配置的参数也是不合理的,真是场景需要根据业务场景自己估计,代码可以直接copy到本地方便直接验证
拒绝策略
这里我们再简单补充一下4中拒绝策略,什么时候抛出处呢?
当我们的最大线程数满了+阻塞队列也满了
AbortPolicy(默认策略)
把上述代码拿来验证下,以下是四种策略运行结果:
ExecutorService threadPool = new ThreadPoolExecutor(
3,5,3,TimeUnit.SECONDS,
new LinkedBlockingDeque(2),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy()
);
这里我们配置默认AbortPolicy策略
运行结果如下所示,直接抛出了异常信息
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
pool-1-thread-3开始执行
pool-1-thread-2开始执行
java.util.concurrent.RejectedExecutionException: Task com.cn.MyThreadDemo$$Lambda$1/1023892928@7699a589 rejected from java.util.concurrent.ThreadPoolExecutor@58372a00[Running, pool size = 3, active threads = 2, queued tasks = 0, completed tasks = 2]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at com.cn.MyThreadDemo.main(MyThreadDemo.java:21)
CallerRunsPolicy 策略
我们发现最后一个任务竟然还是main线程
帮我们执行了,因为CallerRunsPolicy
策略就是,等线程池处理不了的任务,谁提交的任务,就给谁拿回去,让她自己执行,这里发现是main提交的任务,所以最终还是还给了main线程
自己执行
main开始执行
pool-1-thread-1开始执行
pool-1-thread-3开始执行
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
DiscardPolicy 策略
直接抛弃策略,异常也不会抛,什么都不做
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
pool-1-thread-3开始执行
pool-1-thread-2开始执行
DiscardOldestPolicy 策略
该策略下,抛弃进入队列最早的那个任务,然后尝试把这次拒绝的任务放入队列,然后给第一个线程执行
pool-1-thread-1开始执行
pool-1-thread-3开始执行
pool-1-thread-2开始执行
pool-1-thread-3开始执行
pool-1-thread-1开始执行
不过当你配置抛弃策略为什么都不做的话,这样很难发现定位你的问题,所以最好不要用这种抛弃策略
一般线程中最大线程池参数配置多大呢?
- CPU密集型:查看你的cpu核心是几核数,这个最大线程池就是配置成几
- IO密集型: 当你发现你的5个IO执行效率很慢,这个时候你可以开2倍最大核核心线程数,5个处理你的IO效率慢的,还剩下5个处理后面提交的任务,这样不至于阻塞你的后续线程任务