1.1 ThreadPoolExecutor详解
1.1.1 ThreadPoolExecutor类核心属性
-
线程池的创建主要核心类是ThreadPoolExecutor,也就是说,它的一个实例就是一个线程池对象,我们可以new一个对象,来创建初始化一个线程池。
-
而对于ThreadPoolExecutor对象的实例化,有一些核心属性是我们必须要了解和掌握的。
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler){ ... }
| 属性 | 作用 | 类型 |
|---|---|---|
| corePoolSize | 核心线程池大小 | int |
| maximumPoolSize | 最大线程池大小 | int |
| keepAliveTime | 非核心线程的闲置时间,超过这个时间,将被回收 | long |
| TimeUnit | 时间单位 | TimeUnit |
| workQueue | 指定阻塞队列 | BlockingQueue |
| handle | 当线程池和队列已满的情况下,对新任务申请线程的请求采取什么策略进行处理,处理策略举例:直接丢弃该请求/抛出异常等。 | RejectedExecutionHandle |
| threadFactory | 指定线程创建工厂类,也就是常见线程用的 | ThreadFactory |
-
corePoolSize它决定了当前线程池的最小线程创建数量,是不会被清除的。其它属性也非常好理解,这里不作过多解释。
-
主要关注一下workQueue和handle:
-
workQueue指定一个BlockingQueue类型的阻塞队列,它的作用无非就是在当前线程池已满,没有闲置线程可用的情况下,新的任务请求到来后,被临时存放的一个容器,等待空闲出来的线程从中拿出来调用。
-
我们还可以将阻塞队列分为无界和有界两种类型的队列。无界队列有LinkedBlockingQueue,有界队列有ArrayBlockingQueue。(下面Executors的笔记中,会有对这两类队列的分析,实际在看阿里开发手册时,不建议使用Executors来创建线程池,与无界队列有关)
-
handle则指定一个RejectedExecutionHandle,当线程池和阻塞队列已满,新的线程请求时,指定ThreadPoolExecutor对当前这个请求的一个处理策略,例如,直接丢弃或抛出异常等。也就是说这是一个策略类,它的实现策略类包括以下几个:
策略类 处理策略 AbortPolicy 抛出异常,拒绝处理 DiscardPolicy 直接丢弃 DiscardOldestPolicy 丢弃存在最老的任务,并执行新的任务 CallerRunsPolicy 在调用者的线程中执行,否则丢弃之 实际,如果这些策略无法满足我们的实际需求,我们可以实现RejectedExecuttionHandle这个类,自定义一个策略。
-
1.2 Executors创建线程池
-
Executors提供了包括以下三种类型的线程池的实例生产
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() { return new FinalizableDelegatedExecutorService (new ThreadPoolExecutor(1, 1, // 创建核心线程数和最大线程数为1的线程池 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())); }
- newFixedThreadPool
```java
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, // 创建一个固定线程数的线程池
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
- newCacheThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, // 创建一个最大线程数几乎没有限制的线程池
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
-
虽然,通过这样的方式可以方便地创建一些线程池,但是,阿里巴巴开发手册并不建议这样做,它建议我们直接使用ThreadPoolExecutor进行创建,主要的原因在于:
① newSingleThreadPool和newFixedThreadPool这两种方法创建出来的线程池,使用的是无界队列LinkedBlockingQueue,我们可以看到上面的源码使用的是无参构造创建了该队列对象,它默认指定了LinkedBlockingQueue的容量为Integer.MAX_VALUE(注意这里,超大的队列容量),也就是int类型所能达到的最大值,这可能造成在线程池已满的情况下,任务无法及时处理完毕,大量的任务请求一直被压入队列中,会出现OOM。
② 而newCacheThreadPool方法创建的线程池,它也有类似的情况,最大线程数在源码上就可以看出为Integer.MAX_VALUE, 如果大量的线程无法及时处理完任务,一直有新的任务请求就一直创建新线程,占用超过最大内存,也会出现OOM。
-
因此,这两种情况都是不可控和极为不安全的,应该通过ThreadPoolExecutor来主动创建线程池,进行一些合理的优化设置。