理解线程池中七大核心参数,四大拒绝策略有什么作用?

配置线程池参数

代码演示

 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-1thread-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个处理后面提交的任务,这样不至于阻塞你的后续线程任务

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

魔道不误砍柴功

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值