震惊,线程池参数讲的这么明白!!!!

本文详细阐述了线程池的作用,为何引入能提升效率,以及标准库线程池构造方法参数的含义,特别关注了corePoolSize、maximumPoolSize、keepAliveTime、工作队列和线程工厂的设置。作者还讨论了如何根据程序类型(CPU密集型/IO密集型)确定合适的线程数和拒绝策略。
摘要由CSDN通过智能技术生成

一:为什么存在线程池???

线程池存在的最大作用就是提高效率.
并发编程,使用多进程就可以了,
但线程比进程更轻量,在频繁创建销毁的时候,更有优势.
但随着时代的发展,对于"频繁"有了新的定义了.
以前,一个服务器1s处理1k个请求,就认为是时频繁了;
现在,一个服务器1s要处理几万个请求.
现有的要求下,频繁创建销毁线程,开销也变的越来越明显了,
为了使开销变得更小,就引入了线程池.

二:为什么引入线程池之后,能够提升效率??

直接创建/销毁线程,是需要用户态+内核态配合完成的工作,
内核通过调用系统API,创建/销毁线程,但内核完成的工作是不可控的;

而通过线程池,创建/销毁线程,只通过用户态即可,不需要内核态的配合,

如果使用线程池,在创建线程池的时候,就会创建一些线程(提前把线程都创建好),放到用户态代码中写的数据结构中去,后面用的时候,随时从池子里取线程,用完了,就放回池子里面去,这个过程,完全是用户态代码,不需要和内核态进行交互,省去了一些频繁创建/销毁线程的过程.

三:标准库线程池:构造方法参数解析

在这里插入图片描述

3.1:int corePoolSize, int maximumPoolSize


 /**
         *  corePoolSize:核心线程数
         *  maximumPoolSize:最大线程数
         *  标准库中线程是这样设定的:把线程分为两类:
         *  1:核心线程:  corePoolSize
         *  2:非核心线程  ;
         *  maximumPoolSize:核心线程数+非核心线程数
         */

3.1.1:动态扩展

一个线程池,刚被创建的时候,里面就包含核心线程数那么多的线程,比如corePoolSize=4;
线程池会提供一个submit方法,让其他线程把任务提交给他,每个任务就是一个Runnable对象,
如果当前添加的任务比较少,4个线程就足以能够处理,就只有4个线程在工作了,如果添加的任务比较多,4个线程处理不过来了(有很多线程在排队等待执行),这个过程,线程池就会自动创建出新的线程,来支撑更多的任务.(但创建出来的线程总数,不能超过最大线程数).
过了一段时间之后,任务没有那么多了,线程空闲了,部分线程就会被释放掉(回收了),回收只是把非核心线程回收掉,至少保证线程池线程数目不少于核心线程数.
但实际开发中,核心线程数和最大线程数应该设置多少??
不仅仅和电脑配置有关(决定了有多少个CPU核心),更重要的是和程序的实际特点有关.
程序分为两大类:
1:CPU密集型程序
2:IO密集型程序

3.1.2:CPU密集型程序

 Thread t1=new Thread(()->{
            int count=0;
            while(true){
                count++;
            }
        });

形如上述代码:代码逻辑都是在进行算术运算,条件判定,循环判定,函数调用…这种程序一跑,就能立即吃满一个CPU核心.
此时,线程数目,是不应该超过CPU逻辑核心的.

3.1.3:IO密集型程序:

代码大部分时间在等待IO,(等待IO是不消耗CPU,不参与调度的).
代表:标准输入输出,sleep(),操作键盘,操作网络…
由于代码大部分都是在等待,那么瓶颈就不在CPU上,每个线程只消耗CPU一点点,更多考虑的是其他方面(比如网络程序,要考虑网卡带宽的瓶颈(比如网卡是1Gbps,一个线程读写速率是100Mbps,最多搞10个线程))

3.1.4:确定线程数的方法

时间开发中,很多时候,一个程序,逻辑中既包含CPU操作,又包含IO操作,是介于CPU密集和IO密集两者之间的.
我们应该根据实验的方式,找到一个合适的值.
对程序进行性能测试,测试过程中,设定不同的线程池的数值,把程序多运行几次,代码中记录执行时间,同时记录系统消耗的资源(CPU占用/内存占用…),最终根据实际程序的响应速度和系统开销,综合权衡,找到一个你觉得最合适的值.

3.2: long keepAliveTime, TimeUnit unit

   /**
         *   keepAliveTime,非核心线程,允许空闲的最大时间
         *   非核心线程,要在线程不忙的时候,回收掉,但不是立即回收,
         *   只有超过这个最大空闲时间,线程还未工作,才会被回收掉
         *   TimeUnit unit,单位:s,分钟,小时,毫秒........
         */

3.3:BlockingQueue workQueue

线程池的任务队列.
根据实际场景,指定一个合适的capacity的队列进去,也可以使用带有优先级的阻塞队列.
线程池会提供submit方法,让其他线程把任务提交给线程池,线程池内部需要有一个队列这样的数据结构,把要执行的任务保存起来(每个队列存的就是Runnable对象,要执行的逻辑就是run()方法里的内容),后续线程池内部的工作线程,就会消费这个队列,从而来完成具体的任务.

3.4:ThreadFactory threadFactory

3.4.1:工厂模式:

工厂模式,也是一种设计模式.
主要为了解决构造方法太坑了的问题.

   /**
         * 创建一个Point类
         */
        class Point{
            public Point(double x,double y){
                //笛卡尔坐标系下的构造方法
            }
            public Point(double r,double a){
                //极坐标系
            }
        }

创建一个点,在数学中,可以在极坐标系下,也可以在笛卡尔坐标系下.
但此时,上述两个方法不能构成重载,无法通过构造方法,来表示不同的构造点的方式.
工厂模式,核心思路:不再使用构造方法来创建对象.而是给构造方法包装一层.

    class Point{
            //把构造方法包装起来的方法,就称为"工厂方法"
            //这样写代码的套路,就叫做"工厂模式"
            public static Point makePointByXY(double x,double y){
                Point p=new Point();
                p.setX(x);
                p.setY(y);
                return p;
            }
            public  static Point makePointByRa(double r,double a){
                Point p=new Point();
                p.setR(r);
                p.setA(a);
                return  p;
            }
        }

我们还可以用单独的类,来进行提供工厂方法,这个单独的类,就叫作工厂类.

  class Point{
            
        }
        class PointBuilder{
            //把构造方法包装一层的方法,就称为"工厂方法"
            public static Point makePointByXY(double x,double y){
                Point p=new Point();
                p.setX(x);
                p.setY(y);
                return p;
            }
            public  static Point makePointByRa(double r,double a){
                Point p=new Point();
                p.setR(r);
                p.setA(a);
                return  p;
            }
        }

ThreadFactory threadFactory 就是标准库提供的,用来创建线程的工厂类.
这个线程工厂,主要是为了批量的给要创建的线程设置一些属性,线程工厂,在它的工厂方法中,就把线程的属性提前初始化好了.
平时一般不会用ThreadFactory,主要是搭配线程池来使用.

/**
 *    RejectedExecutionHandler handler;拒绝策略
 *    其实是一个枚举类型,规定了采用哪种拒绝策略
 *    如果当前队列满了,仍要继续添加任务,直接阻塞不合适
 *ThreadPoolExecutor.AbortPolicy ,
 * 直接抛出异常,让程序员快速的知道,任务处理不过来了,直接罢工了(之前的任务也不执行了)
 *  ThreadPoolExecutor.CallerRunsPolicy ,线程池不管它,哪个线程添加的任务,哪个线程来执行
 *  ThreadPoolExecutor.DiscardOldestPolicy ,丢弃掉最老的任务,让新的任务去队列中排队
 *  ThreadPoolExecutor.DiscardPolicy  丢弃掉最新的任务(添加任务的线程执不执行不知道),线程池仍然完成原来的任务
 *
 */
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

十一.

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

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

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

打赏作者

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

抵扣说明:

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

余额充值