前文介绍到我们可以通过创建ThreadPoolExecutor对象来定制属于自己的线程池,在创建一个线程池时需要关注核心线程数,最大线程数,拒绝策略,线程构造工厂,任何队列等7个参数,相对而言,灵活度偏高,初次使用的开发者在参数设计和处理时,可能会有困惑,所以java.util.oncurrent并发工具包中也为我们提供了快捷创建线程池的工具类,用于创建常见模版线程,这个类叫做Executors
Executors
Executors中提供的函数及说明如下表所示:
函数名 | 说明 | 备注 |
---|---|---|
defaultThreadFactory() | 返回一个默认的线程工厂,在其内部创建的线程是优先级为Thread.NORM_PRIORITY的非守护线程,线程会被命名为pool-N-thread-M,N代表的是第几个线程工厂,M代表这个工厂生产的第几个线程 | / |
newCachedThreadPool() | 创建一个线程重用的线程池,在该线程池中当任务提交时,会重用已经创建的线程,如果没有线程可用,才会创建新线程,最多可以创建Integer.MAX_VALUE个线程,超过60秒未使用的线程将会被终止并从缓存中移除。通常用于处理时间短,请求密集的场景 | / |
newCachedThreadPool(ThreadFactory threadFactory) | 使用指定线程工厂创建缓存线程池,其他细节与newCachedThreadPool()相同 | / |
newFixedThreadPool(int nThreads) | 创建一个固定容量的线程池,这个线程池的核心线程数=最大线程数=nThreads,也就意味着同时最多有nThreads个线程运行,如果有线程在被关闭前,执行失败终止了,后续有任务需要执行时,会创建一个新的线程,在未主动调用shutdown前,这些线程会一直存在。 | / |
newFixedThreadPool(int nThreads, ThreadFactory threadFactory) | 使用指定线程数和线程工厂创建一个固定容量的线程池,线程池特性和newFixedThreadPool(int nThreads)相同 | / |
newScheduledThreadPool(int corePoolSize) | 使用corePoolSize为核心线程数,创建一个可以定时或周期性执行任务的线程池, | / |
newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) | 使用corePoolSize为核心线程数,threadFactory为线程工厂,创建一个可以定时或周期性执行任务的线程池 | / |
newSingleThreadExecutor() | 创建一个单线程运作的线程池,在该线程池上顺序按照顺序执行,在任意给定时间,最多一个任务在执行 | / |
newSingleThreadExecutor(ThreadFactory threadFactory) | 使用指定的线程工厂创建一个单线程线程池,线程池特性同newSingleThreadExecutor()创建的线程池 | / |
newSingleThreadScheduledExecutor() | 创建一个可以定时或周期性执行任务的单线程池 | / |
newSingleThreadScheduledExecutor(ThreadFactory threadFactory) | 使用指定线程工厂创建一个可以定时或周期性执行任务的单线程池 | / |
newWorkStealingPool() | 创建一个新的具有抢占式操作的线程池,每个线程都有一个任务队列存放任务。其是对ForkJoinPool的扩展,当不传入参数时,默认创建等于处理器个数的线程数 | 大家都知道现在多数设备都是多核处理器,在任务处理过程中,如果单个任务耗时过长则其他核的CPU会处于闲置状态,造成浪费。ForkJoinPool主要是出于充分利用多核CPU特性而构造的线程池,针对一个执行时间较长的任务而言,我们可以将其拆分成若干个子任务,这些子任务分别在CPU的每个核上运行,最后将运行结果整合在一起输出返回 |
newWorkStealingPool(int parallelism) | 使用parallelism创建一个线程池,线程池特性同newWorkStealingPool(),不同的是线程数是parallelism | / |
结合上表,我们可以看出Executors中一共为我们提供了四类常用线程池,主要有:
- CachedThreadPool:缓存线程池
- FixedThreadPool:固定容量线程池
- SingleThreadPool:单线程线程池
- ScheduledThreadPool:周期或定时执行任务的线程池
这些线程池的特性和优缺点如下表所示:
线程池名称 | 核心线程数 | 最大线程数 | 任务队列数据结构/最大长度 | 缺点 | 优点 |
---|---|---|---|---|---|
CachedThreadPool | 0 | Integer.MAX_VALUE | SynchronousQueue | 有可能创建太多线程,导致OOM异常 | 用于执行大量耗时较少的任务 |
FixedThreadPool | nThreads(最小取值为1,最大取值为Integer.MAX_VALUE) | nThreads | LinkedBlockingQueue/Integer.MAX_VALUE | 有可能任务队列中积攒过多任务,引起OOM异常 | 线程数量固定,能够更好的响应外界任务请求 |
SingleThreadPool | 1 | 1 | LinkedBlockingQueue/Integer.MAX_VALUE | 同上 | 顺序执行任务 |
ScheduledThreadPool | corePoolSize(最小取值为0,最大取值Integer.MAX_VALUE) | Integer.MAX_VALUE | DelayedWorkQueue | 有可能创建太多线程,导致OOM异常 | 用于处理定期/定时任务 |
从上表可以看出,对于Executors提供的模版函数构建的线程池而言,由于其等待队列或者最大线程数为Integer.MAX_VALUE,使得其均有OOM异常的风险,故在使用时,应考虑详尽,规避大量任务或线程的创建,如果遇到相关异常,建议使用ThreadPoolExecutor自行实现线程池,限制线程池的线程或任务数量,以避免OOM异常。