主要分为 7 个参数:
- 核心线程数(corePoolSize)
- 最大线程数(maximumPoolSize)
- 保持存活时间(keepAliveTime)
- 存活时间单位(unit)
- 工作队列(workQueue)
- 线程工厂(threadFactory)
- 拒绝策略(handler)
1、corePoolSize(核心线程数):
核心线程会一直存活,即使没有任务需要执行。当线程数小于核心线程数时,即有线程处于空闲中,线程池也会优先创建新线程处理。若设置 allowCoreThreadTimeout=true(默认 false)时,核心线程也会超时关闭
2、maximumPoolSize(最大线程数):
当有新任务进来,而核心线程全部处于工作状态,则会将任务加入到工作队列中等待。若新任务进来时核心线程繁忙,工作队列也已满,线程池会在检查当前池中线程数不超过“maximumPoolSize”的情况下进行扩容。扩容的线程在空闲后会根据过期时间(keepAliveTime)来执行销毁策略。
3、keepAliveTime(保持存活时间):
当线程池中的线程处于空闲状态时,线程池会根据此值来执行销毁策略销毁此线程。当"keepAliveTime=0"时,线程将不销毁,一直保留。
4、unit(过期时间单位):
设置“keepAliveTime”的单位,TimeUnit 值
5、workQueue(工作队列):
当核心线程都在工作时,有新的任务进来,会将新任务放入工作队列中等待,直到有线程空闲下来再调度执行。传入值为“BlockingQueue<Runnable>”的实现类,主要使用的实现类有
- LinkedBlockingQueue:(无界队列)大小不固定的链表阻塞队列,若不指定容量,则默认 Integer.MAX_VALUE,一般称为无界队列,建议指定容量来使用,避免无限排队情况
- ArrayBlockingQueue:(有界队列)FIFO 顺序排序的数组阻塞队列,构造必须指定容量大小。
- SynchronousQueue:缓冲等待队列,不存储元素的阻塞队列,会直接将任务交给消费者,必须等到队列当前元素被消费后才能继续填充元素
- PriorityBlockingQueue:类似于 LinkedBlockingQueue,但是其所含对象的排序不是 FIFO,而是依据对象的自然顺序或者构造函数的 Comparator 决定。
6、threadFactory(线程工厂对象):
用来自定义线程名或者后台线程等设置。可通过实现"ThreadFactory"接口来完成,也可不指定或者使用"Executors.defaultThreadFactory()"来设定默认的线程工厂
7、handler(拒绝策略):
取值为“RejectedExecutionHandler”的实现类。当有新任务时,判断到线程池中的线程数达到"maximumPoolSize"时(核心线程都在工作,队列已满,且线程池中的线程数达到最大限制),线程池会执行拒绝策略,主要有四个策略:
- AbortPolicy:(默认策略)被拒绝的任务被丢弃并抛出异常
- DiscardPolicy:被拒绝的任务将被直接丢弃
- DiscardOldestPolicy:丢弃旧的任务腾出空间来放入新的任务
- CallerRunsPolicy:调用任务 Runnable 的 run()
关于核心线程数的设置,可分为几种情况:
CPU 密集型:
尽量使用较小的线程池,一般为"CPU 的核心数+1"。CPU 密集型任务使得 CPU 使用率很高,开过多的线程数,会增加上下文切换的次数,会带来额外的开销
IO 密集型:
可以使用稍大的线程池,一般为"CPU 核心数的 2 倍"。IO 密集型任务的 CPU 使用率不高,因此可以让 CPU 在等待 IO 的时候去处理别的任务,充分利用 CPU 时间
混合型任务:
可以将任务分成 IO 密集型和 CPU 密集型任务,然后创建不同的线程池来处理。
对于计算密集型的任务,在拥有 N 个处理器的系统上,当线程池的大小为 N+1 时,通常能实现最优的效率。(即使当计算密集型的线程偶尔由于缺失故障或者其他原因而暂停时,这个额外的线程也能确保 CPU 的时钟周期不会被浪费。)-----摘自《JavaConcurrencyInPractise》(8.2 节)