线程池的理解与应用

目录

使用Executors创建线程池

在使用Executors创建不同线程池的时关键参数意义

Executors创建线程池的弊端

使用ThreadPoolExecutor创建线程池

线程池拒绝策略

如何合理配置线程池容量?

计算密集型

IO密集型

高并发下怎么选择最优线程数

动态调整 


使用Executors创建线程池

这里Executors类的静态方法创建了固定数量的线程池,也可以去创建带缓存的线程池、单一线程池

在使用Executors创建不同线程池的时关键参数意义

都在Executors的源码里可见:

corePoolSize 线程池中必须至少活跃core个线程,尽管他们是没有任务做
maximumPoolSize 线程池最大容量
keepAliveTime 线程池中活跃的线程大于core个时,这些线程最多等待KeepAliveTime之后仍没有任务,就可以下班休息了 
workQueue 用于保存没有待执行任务
unit keepAliveTime的单位
threadFactory 用于创建线程
handler 当队列排满了且线程池容量已经达到组最大力,如何拒绝后续请求

Executors创建线程池的弊端

 原因:进入Executors的源码可以看到:

 

Executors 的LinkedBlockingQueue是一个用链表实现的有界阻塞队列,容量可以选择进行设置,默认最大长度为Integer.MAX_VALUE,而上面用newFixedThreadPool中创建线程池时,LinkedBlockingQueue并未指定容量。此时,LinkedBlockingQueue就是一个无边界队列,对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致OOM问题

所以我们在实际生产中,固定线程池、单一线程池、都不用,而是直接使用ThreadPoolExecutor的构造函数来自定义创建线程池,规避这些风险

使用ThreadPoolExecutor创建线程池

public static void main(String[] args) {

    // 直接调用ThreadPoolExecutor的构造函数,指定参数来创建线程池

 ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(2, 5, 2, TimeUnit.SECONDS,

            new ArrayBlockingQueue<Runnable>(2));

    // 提交7个任务,恰好等于恰好等于maximumPoolSize[最大线程数] + capacity[队列大小]

    for (int i = 1; i <= 7; i++) {

        final  int idx = i;

        // ()->{}等于 new Runnable(){}

        poolExecutor.submit(

                ()->{

                    System.out.println(Thread.currentThread().getName()+"处理任务"+idx);

                    try {

                        // 模拟线程执行时间,1s

                        Thread.sleep(10000);

                    } catch (InterruptedException e) {

                        e.printStackTrace();

                    }

                }

        );

        try {

            Thread.sleep(500);

        } catch (InterruptedException e) {

            e.printStackTrace();

        }

    }



}

执行结果:

 

分析执行过程:

最大容量:5work队列是2,所以该线程池最多可接纳7个任务

这里用for循环提交7个任务,假设这里是大于7个任务,那么后面牵扯到对于多余的任务如何处理,就需要使用handler

提交任务1,直接创建Thread1来服务

提交任务2,直接创建Thread2来服务

因为coreThread是2

提交任务3,到work队列排队

提交任务4,到work队列排队

此时coreThread都在工作,work队列也排满了

提交任务5,创建Thread5服务

提交任务6,创建Thread6服务

提交任务7,创建Thread7服务

此时已经达到线程池最大容量,所以对于work队列中排队的任务,需要等core和其他工作结束后,再服务

先拿任务3服务,再拿任务4服务

线程池拒绝策略

看下面这个例子:

当我们没有指定拒绝策略的时候,默认是AbortPolicy,对于额外的任务直接抛出异常

 

当我们使用CallerRunsPolicy策略时,对于额外的任务,不会丢弃不会抛异常,而是回调给调用者处理该任务

当我们使用DiscardOldestPolicy策略时,会丢弃队列中等待时间最长的任务,然后执行额外的任务

当我们使用Discard策略时,对多出的任务直接丢弃,不报异常

 

 

如何合理配置线程池容量?

计算密集型

计算密集,我们肯定是要CPU充分利用起来,所以对Ncpu的系统,通常创建Ncpu+1个线程的线程池来获得最优利用率

加入计算密集型线程恰好在某一页发生错误而暂定,我们就还有一个额外的线程来处理,可以确保这种情况下cpu不会中断工作

IO密集型

举个例子:文件的上传下载就是IO密集型,阻塞耗时比计算多的多。IO型的一般都会对网络带宽要求比CPU处理要求高。

我们需要考虑系统内存、需要考虑在服务器上创建几个线程比较合适

 

生产中一般会有几百万几万的高并发,其实后台都是这些线程在轮询的,轮到谁执行谁。比如百度网盘或者迅雷下载文件的时候

在迅雷下载电影的时候可以选择线程数一个文件原来只有一个线程下载下载分5个线程下载,当然会快,但他到最后99%的时候可能会卡一下,这是因为他需要把5个线程下载的文件做合并

高并发下怎么选择最优线程数

实际应用中,如何设置并发线程的数量?

假设一个系统的TPS(TransactionPerSecond每秒处理事务量)是20,一个Transaction由一个线程完成,每个线程处理一个Transaction的时间是4s,

设计多少个线程,使得1s内处理完20个Transaction??

解:一个线程1s内处理1/4=0.25个事务,20个事务1s内完成需要:20/0.25=80个线程

动态调整 

线程演变为协程

动态调整 

不同情况不同调整

1、任务少,活跃线程数少,可以适当降低线程数量,节省系统资源

2、若队列内排队任务非常多,甚至满了

        若系统负载不高,可以考虑渐进式增加线程数量来处理任务、

        若系统负载尚可,可以考虑水平扩展,增加机器数量

        若系统负载高,降低每个机器的线程数量,增加机器的数量

3、若存在脉冲流量

        若系统内存充足,可以考虑扩大等待队列

        若系统内容不足,可考虑增加机器节点

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值