美文网首页
9.线程池-2

9.线程池-2

作者: nieniemin | 来源:发表于2021-08-04 15:32 被阅读0次

    一、常见线程池介绍

    上一节中线程池使用到了Executors.newSingleThreadExecutor()方法。接下来介绍下常用的线程池。

    newSingleThreadExecutor
    • newSingleThreadExecutor():创建一个单线程化的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
    newFixedThreadPool
    • newFixedThreadPool(int nThreads):创建一个可重用固定个数的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
    newCachedThreadPool
    • newCachedThreadPool(): 推荐使用。可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务

    • newScheduledThreadPool(int corePoolSize):创建一个定长线程池,支持定时及周期性任务执行。

    在性能上newCachedThreadPool>newFixedThreadPool>newSingleThreadExecutor。通过截图可以看到这三个方法都调用了ThreadPoolExecutor的构造方法,在执行Executors.线程池方法时,idea会提示


    给我们的解释如下:

    线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
    1)FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
    2)CachedThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

    因此在使用线程池时,我们也要放弃前面使用Executors类创建线程池,采用ThreadPoolExecutor类来创建线程池。

    二、ThreadPoolExecutor构造函数参数介绍

    ThreadPoolExecutor构造器共有7个参数:

    • corePoolSize: 核心线程数

    • maximumPoolSize:池中允许的最大线程数

    • keepAliveTime: 超时时间,当线程数大于核心数时,当空闲时间达到keepAliveTime时,线程会被销毁,直到只剩下corePoolSize个线程

    • TimeUnit: 超时时间的单位,秒,毫秒,微秒,纳秒等,与keepAliveTime参数共同决定超时时间。

    • workQueue:表示如果任务数量超过核心池大小,多余的任务添加到阻塞队列中

    • threadFactory:线程工厂

    • handler:线程池对拒绝任务的处理策略

    下图简单介绍了核心线程数、最大线程数工作,分析下核心线程数和最大线程数的关系,有助于加深理解:

    (1)当提交一个任务时,先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,线程池创建一个新线程(worker)执行任务(task)且任务结束后将该线程保留在线程池中,不做销毁处理。除非设置了allowCoreThreadTimeOut=true时,无论线程数多少,那么线程处于空闲状态超过一定时间(keepAliveTime)就会被销毁掉。

    (2)如果达到了corePoolSize,判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中

    (3)如果workQueue满了,判断线程池中的线程数量是否达到了maximumPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。注意: 在线程池中的线程数量超过corePoolSize时,对于右侧的非核心线程数每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。

    线程池工作

    workQueue
    用来保存等待被执行的任务的阻塞队列,且任务必须实现Runnable接口,阻塞队列如下:

    • ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
    • LinkedBlockingQuene:基于链表结构的无界阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
    • PriorityBlockingQueue:使用平衡二叉树,实现的具有优先级的无界阻塞队列
    • DelayQueue:无界阻塞延迟队列,队列中每个元素均有过期时间,当从队列获取元素时,只有过期元素才会出队列。队列头元素是最块要过期的元素。
    • SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;

    饱和策略
    当工作队列满且线程个数达到maximunPoolSize后所采取的策略

    • AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
    • CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
    • DiscardPolicy:丢弃新的任务,且不抛出异常。
    • DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
      自定义策略:根据用户需要定制。

    参考:
    https://zhuanlan.zhihu.com/p/87733949
    https://blog.csdn.net/qq_33323054/article/details/106923732

    相关文章

      网友评论

          本文标题:9.线程池-2

          本文链接:https://www.haomeiwen.com/subject/naxyvltx.html