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

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

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:
* {@code corePoolSize < 0}
* {@code keepAliveTime < 0}
* {@code maximumPoolSize <= 0}
* {@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 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核数 / 阻塞系数。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/877561.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存