JUC之线程池(总体第四篇)

1、线程池的优势

(1)概念

  • 之前我们在操作线程的时候都是new Thread,然后使用,然后释放(存在问题就是要加载,释放资源,会浪费资源)。现在不一样了,就我线程池给你new好,准备好,你要使用的时候调用,不用了就释放就好。
  • 同样的JDBC数据库连接池:也是之前学习的概念,连接池给你new准备好,然后你要使用的时候调用即可。
  • 还有的springIOC容器,也是同样的概念。

(2)为什么用线程池

在这里插入图片描述

2、如何使用线程池

(1)获取线程池

在这里插入图片描述
常用的ExecutorService接口。
在这里插入图片描述

  • 我们的Arrays与Collection都有工具类
    在这里插入图片描述
    我们线程池也肯定有工具类ExecutorService executorService = Executors.newFixedThreadPool(5);//一池五个受理,还有其他的方法
    在这里插入图片描述

(2)架构原理

在这里插入图片描述
开始中使用的就是ThreadPoolExecutor,如果面试问对线程池的理解,那就是对这个类的理解。如何拿到这个类呢,就要通过旁边没有关系的Executors

(3)常用方法调用

  • 固定的newFixedThreadPool:它适用于长期任务性能好,创建一个线程池,一池有N个固定的线程,有固定线程数的线程。
    在这里插入图片描述
    同一个线程能多次使用。
`ExecutorService executorService = 
                Executors.newFixedThreadPool(5);//一池五个受理`
  • 一池一个类
    在这里插入图片描述
    感觉有点多此一举,但是它也会有用到的时候。
 ExecutorService executorService =
                Executors.newSingleThreadExecutor();//单例模式
  • 一池可扩容线程
    在这里插入图片描述
    可以扩展的线程池,当你业务多的时候就多new一点
ExecutorService executorService =
                Executors.newCachedThreadPool();//一池多线程

3、ThreadPoolExecutor原理

我们上一步使用了三种常见的线程池获得的方法中底层都是使用了ThreadPoolExecutor
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 还使用了阻塞队列
    在这里插入图片描述

4、线程池底层原理(线程池的七大参数)

 /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

(1)corePoolSize

  • 线程池中的常驻核心线程数,即线程数。
  • 其实就是一个线程池里面有多少个线程。

(2)maximumPoolSize

  • 线程池中能够通纳同时的最大线程值,此值必须大于一。
  • 就是一个业务特别忙的时候,它就会扩容,扩到这个值。

(3)keepAliveTime

  • 多余的空闲线程的存活时间,当前池中线程数量超过corePoolSize时,当空闲时间达到keepAliveTime时,多余线程就会被销毁直到只剩下corePoolSize 个线程为止。
  • 相当于线程的生命周期,就是当你之前的业务太忙了,现在闲下来了,就再等keepAliveTime的时间,之后就把这些销毁。

(4)unit

  • keepAliveTime的单位。

(5)workQueue

  • 任务队列,被提交但未被执行的任务。
  • 这里就是个阻塞队列:其实就可以理解为候客区一样的概念(当前的线程处理不完请求,剩下的请求就会进入候客区)。

(6)threadFactory

  • 表示生成线程池中工作线程的线程工厂,用于创建线程,一般默认即可。
  • 用于创建线程的
    在这里插入图片描述

(7)handler

  • 拒绝策略,表示当队列满了,并且工作线程大于等于线程池的最大线程池数(maximumPoolSize)时如何来拒绝请求执行的Runnable的策略。

5、线程池的底层工作原理1

(1)对上面参数的动态理解

  • 如果来请求并且候客区也不满的情况
    在这里插入图片描述
    来了就给你处理,如果没有corePoolSize的线程给你工作,那你就在候客区等。

  • 如果继续来人(来了6,7,8)
    在这里插入图片描述
    此时就要扩容了,把你的常驻线程增加到manximumPoolSize。
    在这里插入图片描述
    此时就可以把3,4,5处理了,然后6,7,8进入候客区。

  • 假设又有人来了(这次来了10个)
    当值窗口和扩容窗口以及候客区都满了,此时就要进行拒绝的Handler来进行处理了。

  • 当你1和2,忙完了就走了,然后6和7就去当值窗口进行办理,然后3,4,5都接着办理完成走了,然后此时加班的窗口的keepAlive就要进行计时了,当到了时间后,就要处理销毁了。
    在这里插入图片描述

  • 其实就可以理解如下了
    在这里插入图片描述
    首先提交任务,任何来就要判断核心线程是否有空闲,空闲就接客。如果继续来人,就要在候客区去等。如果继续来人(候客区也满了),就扩容。
    在这里插入图片描述

(2)线程池用哪个,开发中如何设置参数

  • 常用的肯定是:我感觉是使用可扩容的,but阿里巴巴开发手册里面说啦
    在这里插入图片描述
    那为什么不适用JDK自带的呢,其实就是如下的原因,21亿的内存,你直接给干爆了
    在这里插入图片描述

  • 开发的参数如下

6、线程池的底层工作原理2

在这里插入图片描述

(1)手写线程池

在这里插入图片描述
前面几个参数都比较简单,那最后一个参数的处理策略就要自己思考了,而且其中的new LinkedBlockingQueue它的我们必须设置大小,不然也很危险
在这里插入图片描述

  • 看看实现案例
    在这里插入图片描述
    我们自定义的线程池能使用
    • 继续测试
      在这里插入图片描述
      增加一个就爆炸了 、它的最大值和我们的队列数。

(2)拒绝处理策略

在这里插入图片描述
默认的new ThreadPoolExecutor.AbortPolicy,它直接给报错了。

在这里插入图片描述
在这里插入图片描述

  • AbortPolicy:阻止系统正常执行
    在这里插入图片描述

  • CallerRunsPolicy:回退找原来的(之前的谁让你来找我的,你就回退过去找他)
    在这里插入图片描述
    main线程调用了我,回退给main。而且它还有可能是多个都会退给main处理。

  • DiscardPolicy:Discard抛弃,丢弃无法处理的任务
    在这里插入图片描述

  • DiscardOldestPolicy:抛弃等待最久的那个。
    在这里插入图片描述

(3)如何确定线程池的数量呢

在这里插入图片描述
看你的业务是IO密集型,还是CPU密集型。

  • 如果是CPU密集型的话,那就CPU加1来设置。
    在这里插入图片描述
  • IO密集型:CPU核数 / 阻塞系数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值