线程池ThreadPoolExecutor

一、线程池介绍

            在web开发中,服务器需要接受并处理请求,所以会为一个请求来分配一个线程来进行处理。如果每次请求都新创建一个线程的话实现起来非常简便,但是存在一个问题:

          如果并发的请求数量非常多,但每个线程执行的时间很短,这样就会频繁的创建和销毁线程,如此一来会大大降低系统的效率。可能出现服务器在为每个请求创建新线程和销毁线程上花费的时间和消耗的系统资源要比处理实际的用户请求的时间和资源更多。

          那么有没有一种办法使执行完一个任务,并不被销毁,而是可以继续执行其他的任务呢?

          这就是线程池的目的了。线程池为线程生命周期的开销和资源不足问题提供了解决方案。通过对多个任务重用线程,线程创建的开销被分摊到了多个任务上。

         什么时候使用线程池?

               单个任务处理时间比较短

               需要处理的任务数量很大

        使用线程池的好处

              降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

              提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。

              提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

 

二、核心构造方法讲解

 

参数说明: 

     corePoolSize:核心线程池大小

     maximumPoolSize:最大线程池大小

     keepAliveTime:线程池中超过corePoolSize数目的空闲线程最大存活时间

     unit:keepAliveTime的单位(纳秒、毫秒、秒、分、时、天等,使用TimeUnit中的常量)

     workQueue:线程等待队列(阻塞任务队列)

                 可选值有:ArrayBlockingQueue: 有边界的阻塞队列。长度大小初始化时制定,即内部由数组实现

                                   LinkedBlockingQueue: 有边界的阻塞队列。边界是可选的,如果初始化的时候不指定则默认是Interger.MAX_VALUE,内部由链表实现。

                                   PriorityBlockQueue: 带有优先级的阻塞队列。没有边界。       

     threadFactory表示创建线程的工厂:可用默认的Executors.defaultThreadFactory(),也可以自定义一个类,实现ThreadFactory接口并重写newThread即可。

     handler:拒绝策略(当提交任务数超过maxmumPoolSize+workQueue之和时,任务会交给RejectedExecutionHandler来处理)

                 默认有四种方式:ThreadPoolExecutor.AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException。

                                          ThreadPoolExecutor.CallerRunsPolicy:调用线程直接call该任务的execute本身。

                                          ThreadPoolExecutor.DiscardPolicy:将任务删除。

                                          ThreadPoolExecutor.DiscardOldestPolicy删除工作队列头部任务。

               当然也可以自定义拒绝策略(定义一个类实现RejectedExecutionHandler接口,重写rejectedExecution方法即可)。

 

三、execute方法工作原理

      当一个任务通过execute(Runnable)方法欲添加到线程池时

      1、如果线程池中运行的线程 小于corePoolSize ,即使线程池中的线程都处于空闲状态,也要 创建新的线程 来处理被添加的任务。

      2、如果线程池中运行的线程大于等于corePoolSize,但是缓冲队列 workQueue未满 ,那么任务被放入缓冲队列 。

      3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满(即无法将请求加入队列),并且线程池中的数量小于maximumPoolSize,建新的线程 来处理被添加的任务。

      4、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize ,那么通过 handler 所指定的策略来处理此任务。

      5、当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止 。这样,线程池可以动态的调整池中的线程数。

      也就是:处理任务的优先级为:corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。 

 

      注意点:

 

  • 线程池初始化时,是空的。
  • 如果阻塞队列已满,且当前线程数<maximumPoolSize,则新建线程执行该任务。而不是新建线程,从阻塞队列里take任务来执行,所以这里并不是先来先执行的。

 

四、线程池的监控

      1、getTaskCount:线程池已经执行的和未执行的任务总数。

      2、getCompletedTaskCount:线程池已完成的任务数量,该值小于等于taskCount。

      3、getLargestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize。

      4、getPoolSize:线程池当前的线程数量。

      5、getActiveCount:当前线程池中正在执行任务的线程数量。

 

五、Executors 工厂方法

       1、newSingleThreadExecutor:单个后台线程(其缓冲队列是无界的),创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

      2、newFixedThreadPool固定大小线程池    (其缓冲队列是无界的)  ,创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

      3、newCachedThreadPool:无界线程池,可以进行自动线程回收,创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

      4、newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

 

 

      参考:https://www.cnblogs.com/liuzhihu/p/8177371.html

                 https://uule.iteye.com/blog/1123185

posted @ 2019-05-06 23:36  苏子榆  阅读(207)  评论(0)    收藏  举报