线程池查漏补缺

tomcat线程池和jdk线程池区别

概述

  1. 线程池是什么,为什么要线程池
  2. jdk有哪些线程池和原理
  3. 第三方中间件的线程池特点
  • tomcat线程池
  • quartz线程池
  1. 线程池展望

线程池是什么,为什么要线程池

我们在项目启动后,因为CPU是多核的,为了充分利用CPU,可能需要启动多个线程去处理任务。频繁的创建销毁线程是有开销的。所以需要创建线程后缓存住,让线程可以频繁的处理各种任务。这个时候就需要用线程池。线程池一般会缓存住N个线程,然后往线程池里面扔任务Runnable,线程不断的处理任务。处理完了后停留在那里等待。

jdk有哪些线程池和原理

jdk线程池主要入口是在java.util.concurrent.Executors,其中包含

  • Executors.newFixedThreadPool 固定核心数的线程池,有多余任务往无界队列中放

  • Executors.newSingleThreadExecutor 单线程的线程池,有多余任务往无界队列中放

  • Executors.newCachedThreadPool 有任务来就开辟新的线程,不缓存任务,不缓存线程。

  • Executors.newSingleThreadScheduledExecutor 单线程的延迟线程池,也就是说可以指定任务N秒后周期性执行。

  • Executors.newScheduledThreadPool N线程的延迟线程池,也就是说可以指定任务N秒后周期性执行。

  • Executors.unconfigurableExecutorService

线程池的主要原理是在ThreadPoolExecutor这个类中。ThreadPoolExecutor有以下概念

  • corePoolSize

    扫描二维码关注公众号,回复: 5212066 查看本文章
  • maximumPoolSize

  • keepAliveTime

  • workQueue

这四个概念一起解释,corePoolSize指的是线程池核心数,当有任务进来的时候,如果开辟的线程少于该值,就不断开新线程去处理,直到该值。如果到了该值之后,就把新的任务存放在队列中,也就是workQueue中,去存放的过程会成功和失败,如果成功就存放在队列中了,如果失败,就继续开辟线程去执行。直到线程数等于maximumPoolSize,便不再开新线程。如果队列满了,线程也到最大值了,这个时候,就进行拒绝策略。

  • AbortPolicy 默认策略,抛出异常RejectedExecutionException。

  • DiscardPolicy 什么都不做

  • DiscardOldestPolicy 把任务头丢掉,然后再把任务加入队列

  • CallerRunsPolicy 主线程自己去执行任务

  • RejectHandler 自己实现

原理大致说明。ThreadPoolExecutor这个类除了参数那些参数外,还有HashSet<Worker> workers,参数,该参数和线程一一对应,也就是说线程池里面的线程就是worker,当任务进来后会和corePoolSize比较,继而新建或者申请worker。新建的线程会不断的从任务队列去拿任务,拿任务有超时时间,就是刚才说到的keepAliveTime,到达时间后,如果说当前线程数大于corePollSize,则自己退出该线程。没有大于则继续循环运行,继续循环运行就是在死循环中去拿队列。

第三方中间件的线程池特点

tomcat线程池

tomcat的线程池是StandardThreadExecutor类,该类的属性也是ThreadPoolExecutor,不过是把ThreadPoolExecutor动态设置参数暴露出来了。所以StandardThreadExecutor类可以动态的修改核心数,最大数。并且该类的阻塞队列使用的是继承自LinkedBlockingQueue的TaskQueue。TaskQueue的核心在于,只要运行当前池子的线程小于maximumPoolSize,就拒绝你加入队列。我们知道线程的数量和IO密集有关系,tomcat这种IO密集型的中间件,适合更多的线程。这里的线程数是maxThreads,该值默认200,tomcat可以配置。

public boolean offer(Runnable o) {
       //这个类是TaskQueue的的方法,我们知道,线程池,是先加入队列,失败后再开线程直到max数。
        if (parent==null) return super.offer(o);
        // 若运行线程为最大线程数,说明线程已经最大了,没办法,还是扔队列吧。
        if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
        //若任务数小于线程数,直接扔队列,扔队列后,那些闲着的线程自然会取。没必要返回false,让其再重开线程。
        if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
        // 发现运行线程数少于池最大线程数,拒绝加入队列,让线程池继续开线程。
        if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
        //if we reached here, we need to add it to the queue
        return super.offer(o);
    }

quartz线程池

quartz线程池是在org.quartz.simpl.SimpleThreadPool

该线程池非常简单,初始化的时候新建N个线程。这个线程是在线程池初始化的时候就新建。然后把线程放在availWorkers中,如果有任务进来,就从availWorkers拿出1个线程放进busyWorkers,然后把任务给该线程执行。执行完了之后返回给availWorkers中。如果线程满了,主线程扔任务的时候,会检查availWorkers的size,当size<1的时候,停半秒继续检查availWorkers直到有可用才运行任务。

线程池展望

猜你喜欢

转载自blog.csdn.net/weixin_34138255/article/details/87120560