项目里应该常用这个类吧,为了避免频繁的创建线程和销毁线程,如果线程太多也会存在线程之间竞争资源的问题,我们可以通过线程池来避免这些问题
ThreadPoolExecutor 这个是阿里java开发文档上指定的创建线程池的方式,为了避免使用Executors过多的隐藏了线程池的特点,在此需要手动的配置一下线程池的参数。
ThreadPoolExecutor 最多可配置7个参数,分别是
int corePoolSize 线程池核心线程数
int maximumPoolSize 线程池最大线程数(核心线程数+非核心线程数)
long keepAliveTime 线程闲置状态存活时间
TimeUnit unit 线程闲置状态存活时间单位
BlockingQueue<Runnable> workQueue 工作队列
ThreadFactory threadFactory 创建线程的方式
RejectedExecutionHandler handler 拒绝策略
下面介绍一下每个参数的含义
corePoolSize:核心线程数最大值,当当前线程池的线程数小于该值是创建的线程属于核心线程,大于该值时属于非核心线程。核心线程会一直存活于线程池里,即使什么事情也不干,但如果allowCoreThreadTimeOut设置为true的时候,线程池就可以将核心线程在到达闲置最长时间后回收销毁。
maximumPoolSize:线程池最大线程数,值 = 核心数 + 非核心数,非核心线程在闲置到最大闲置存活时间后会被销毁回收。但是核心线程只有在allowCoreThreadTimeOut = true的时候才会回收销毁
keepAliveTime :线程闲置最长存活时间
unit:线程闲置最长存活时间的单位,是个枚举值
public enum TimeUnit {
/**
* Time unit representing one thousandth of a microsecond
* 纳秒
*/
NANOSECONDS
/**
* Time unit representing one thousandth of a millisecond
* 微秒
*/
MICROSECONDS
/**
* Time unit representing one thousandth of a second
* 毫秒
*/
MILLISECONDS
/**
* Time unit representing one second
* 秒
*/
SECONDS
/**
* Time unit representing sixty seconds
* 分
*/
MINUTES
/**
* Time unit representing sixty minutes
* 时
*/
HOURS
/**
* Time unit representing twenty four hours
* 天
*/
DAYS
workQueue:工作队列,用于保存等待执行的任务的阻塞队列。当所有核心线程都在执行的时候,多余的任务就会存放在队列里,如果队列满了就会创建非核心线程去执行任务。常用的队列有以下几种:
1. LinkedBlockingQueue:基于链表结构的队列,基本属于无限长度,最大值为Integer最大值,此队列按FIFO (先进先出) 排序元素,吞吐量通常要高于ArrayBlockingQueue,这个是用的最多的工作队列了,可以存放很多任务,不过需要注意的是一旦采用此队列,你设置的maximumPoolSize就无效了,因为工作队列基本无限,所以队列就不会满,也就不会去创建非核心线程去执行任务,总线程数也就不会大于corePoolSize。
2、SynchronousQueue:一个不存储元素的阻塞队列,因为不存储任务,所以当核心线程都在工作的时候,必须需要创建新的非核心线程去执行任务,然而这时候maximumPoolSize就需要足够大,防止线程池提示无法创建新线程,Executors.newCachedThreadPool就是采用此队列实现的,设置了maximumPoolSize = Integer.MAX_VALUE
3、ArrayBlockingQueue:有界的队列,基于数组实现,该队列可能存放满。就会通过创建非核心线程执行任务,但是总线程数达到maximumPoolSize后也会出现异常,不怎么用
4、DelayedWorkQueue:延时队列,Delayed元素的一个*阻塞队列,只有在延迟期满时才能从中提取元素 不怎么用,不熟悉
threadFactory:线程实现方式,这是个接口,可以实现自己的想要的线程实现方式
handler:拒绝策略,当核心线程数无法执行任务,工作队列也满了,也没有多余的非核心线程数去执行任务等等情况,这时候需要对任务有个处理,ThreadPoolExecutor提供了四种拒绝策略。
1、CallerRunsPolicy:直接运行这个任务,不在线程池里执行。
2、AbortPolicy:抛出RejectedExecutionException异常
3、DiscardPolicy:不抛异常,也不会执行这个任务,也没有任何日志记录
4、DiscardOldestPolicy:先检查ThreadPoolExecutor的等待队列,将队头的任务强行取出来再试图将被拒绝的任务提交给线程池执行
另外Executors提供了四种常用线程池,分别是newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool、newScheduledThreadPool。不过一般项目里不让用,都会自己通过ThreadPoolExecutor自己创建。
线程池的总线程数是需要自己去评估大小的,不能盲目的以为线程数越多越好,要考虑服务器的性能去综合考虑最大线程数。可以参考这个公式 最佳线程数目 = (线程等待时间与线程CPU时间之比 + 1)* CPU数目