简单聊聊 ThreadPoolExecutor 的使用和注意事项,这里不涉及源码的分析。
一、创建一个线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
参数说明:
- @param corePoolSize 核心线程数
- @param maximumPoolSize 最大线程数
- @param keepAliveTime 非核心线程数,保持活跃时长。
- @param unit keepAliveTime 时长单位
- @param workQueue 工作队列
- @param threadFactory 线程工厂
- @param handler 拒绝策略
ThreadPoolExecutor 有四个构造器,其他三个都是调用了上面这个构造器。前五个参数是必须的,线程工厂和拒绝策略有默认实现。
ThreadPoolExecutor 对线程池和队列的使用方式如下:
新创建的线程池默认是没有运行线程的。可以通过调用
- prestartCoreThread() (预创建一个核心线程)
- prestartAllCoreThreads()(预创建所有核心线程)
- execute(Runnable command) (手动创建线程)
注意execute 参数command 不能为空。他会在创建线程的同时,在工作队列的添加一个
command。
二、线程池的拒绝策略
当我们调用execute 向线程池添加一条线程时,当线程池满了和工作队列都满了的时候。线程池无法在添加任务,这时候线程池会拒绝添加任务。这时候回调用RejectedExecutionHandler 的拒绝策略。
public interface RejectedExecutionHandler {
/**
* 当线程池无法接受任务时,拒绝的策略。
*
* @param 请求执行的可运行任务
* @param 线程池
*/
void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}
RejectedExecutionHandler 为所有拒绝策略的基类,已知的拒绝策略有四种:
(1) AbortPolicy 默认的拒绝策略
直接抛出异常RejectedExecutionException
(2) DiscardPolicy
不做任务处理
(3) DiscardOldestPolicy
尝试将工作队列头部移除,在调用executor.execute()方法
(4) CallerRunsPolicy
直接调用r.run();
三、Executors 四种创建线程方法及使用场景
(1) newFixedThreadPool - 线程池大小固定,任务队列无界
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue());
从以上创建线程池的代码可以看出核心线程池和最大线程池保持一致,所以所有的线程都是核心线程。所以活跃时间这里设置是无效的。另外这里工作队列使用了LinkedBlockingQueue无界队列,可以无限添加任务,永远不会拒绝新的任务。
(2) newSingleThreadExecutor - 线程池大小固定为1,任务队列无界
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue()));
与newFixedThreadPool 相似,只不过线程大小被固定为1. 适用于在逻辑上需要单线程处理任务的场景,同时无界的LinkedBlockingQueue保证新任务都能够放入队列,不会被拒绝;缺点和FixedThreadPool相同,当处理任务无限等待的时候会造成内存问题。
(3) newCachedThreadPool - 线程池无限大(MAX INT),等待队列长度为1
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue())
SynchronousQueue是一个只有1个元素的队列,入队的任务需要一直等待直到队列中的元素被移出。核心线程数是0,意味着所有任务会先入队列;最大线程数是Integer.MAX_VALUE,可以认为线程数量是没有限制的。KeepAlive时间被设置成60秒,意味着在没有任务的时候线程等待60秒以后退出。CachedThreadPool对任务的处理策略是提交的任务会立即分配一个线程进行执行,线程池中线程数量会随着任务数的变化自动扩张和缩减,在任务执行时间无限延长的极端情况下会创建过多的线程,导致内存溢出。
(4) newScheduledThreadPool -定时类线程池
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1, threadFactory));
其实是创建了一个ScheduledThreadPoolExecutor 的委托类,接口如下。
public ScheduledFuture> schedule(Runnable command,long delay, TimeUnit unit);
public ScheduledFuture schedule(Callable callable,long delay, TimeUnit unit);
public ScheduledFuture> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);
public ScheduledFuture> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);
具体没用过,可以看出是实现定时或者延时功能设计的线程池。