ThreadPoolExecutor可以通过制定的一些参数来创建一个线程池,目前默认可以通过Executor来实例化四种线程池的执行器,简单的做一下说明:
newFixThreadPool | 创建一个固定长度的线程池,每当提交一个任务就创建一个线程,知道达到线程池的最大数量,这时线程池的规模将不再变化 |
newCacheTheadPool | 创建一个可缓存的线程池,如果线程池的当前规模超过了处理的需求,那么将回收空闲的线程,而当需求增加时,则可以添加新的线程,线程池的规模不存在限制 |
newSingleThreadExecutor | 创建单个工作线程来执行任务,如果这个线程异常结束,会创建另一个线程来替代,确保依照任务在队列中的顺序来串行执行任务 |
newScheduledThreadPool | 创建固定长度线程池,而以延时或者定时的方式来执行任务 |
(自《java并发编程实战》)
我们知道,这些线程池实现的功能都是通过配置ThreadPoolExecutor不同的参数来实现的,在实际业务中我们往往需要按照自己的业务定制化一个线程池,这样的话以上四种线程池也许就不那么适合自己了(阿里代码规范也强烈推荐手动创建线程池)。需要定制化自己的线程池,首先就要了解源码是怎么实现的,以上四种线程池是很好的源码学习对象,也当做是JDK给我们的一个Demo了。
首先先介绍下ThreadPoolExecutor参数
/**
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime 当线程数大于核心线程数的时候,闲置的线程等待任务的最长时间
* @param unit 时间单位
* @param workQueue 工作线程队列(里面存放着待执行的任务)
* @param threadFactory 创建线程的线程工厂
* @param handler 当工作队列因为队列满了而堵塞的时候的处理方式
*
*/
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
}
1、newFixThreadPool
首先我们看看底层参数是如何配置的
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
点进去看一看:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
newFixThreadPool只有一个参数nThread,配置了ThreadPoolExecutor的corePoolSize和maximumPoolSize参数,这里采用的任务队列为LinkedBlockQueue,我们知道,LinkedBlockQueue设置的容量为最大值,所以可以看作是无界限的堵塞队列,意味着我可以无限的在该队列中添加任务,直到把内存撑破。
当我们一直往线程池中拿线程直到当前线程poolSize=corePoolSize的时候,以后的线程将放入任务队列中阻塞等待,而不会创建新的线程,为什么?因为任务队列是无界的,所以永远不会满,所以永远不会触发创建新线程的条件:当队列满了的时候并且当前线程数小于最大线程数,则创建新的线程。
因为线程数永远等于核心线程数,所以keepAliveTime这个参数没有实际意义。
2、newCacheTheadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
看看任务队列是怎么创建的
/**
* Creates a {@code SynchronousQueue} with nonfair access policy.
*/
public SynchronousQueue() {
this(false);
}
/**
* Creates a {@code SynchronousQueue} with the specified fairness policy.
*
* @param fair if true, waiting threads contend in FIFO order for
* access; otherwise the order is unspecified.
*/
public SynchronousQueue(boolean fair) {
transferer = fair ? new TransferQueue<E>() : new TransferStack<E>();
}
这里传入的是非公平任务队列,所以创建的是一个TransferStack,所以不满足任务FIFO的特性。
相反,如果创建的是公平的队列,则创建一个TransferQueue,这里不继续分析了,只需要知道这里创建的是一个非公平的任务队列。
当任务第一次进来的时候,由于这里corePoolSize配置的是0,所以这个任务发现根本就没有线程可以用,于是乎加入到任务队列中,由于任务队列SynchronousQueue是没有容量的,所以该任务进入队列后计算发现poolSize<maximumPoolSize(这里maximumPoolSize设置为Integer.MAX_VAVLUE),于是立即从队列中拿出创建一个新的线程。
之后,每次任务来的时候都按照以上的逻辑创建一个新的线程,因为永远都满足线程的创建条件poolSize<maxmumPoolSize。
当任务被执行完成之后,把线程还给线程池,此时该线程是被闲置的在等待下一个任务,如果等待keepAliveTime的时间后(在这里配置是60s),该线程将被回收。
3、newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
这里用的任务队列是DelayedWorkQueue,它是一个优先级队列,会根据任务的延时时间进行排序,时间短的任务会排在前面。
newScheduledThreadPoolExecutor有四个关键的方法,我们分析其中的一个
/**
* 指定时间后执行任务
*
*/
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
这里根据延时参数delay首先创建一个ScheduledFutureTask,然后根据任务生成一个RunnableScheduledFuture进行任务的延迟执行。看看执行方法delayedExecute
private void delayedExecute(RunnableScheduledFuture<?> task) {
//如果线程池关闭,则拒绝任务
if (isShutdown())
reject(task);
else {
//在延时任务队列中增加任务
super.getQueue().add(task);
//如果线程池关闭并且
if (isShutdown() &&
//判断是否可以继续执行任务
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
//确保至少有一个线程池中有工作线程
ensurePrestart();
}
}
void ensurePrestart() {
//获取线程池中线程的数量
int wc = workerCountOf(ctl.get());
//即使corePoolSize为0也要创建新线程执行空任务
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
当一个任务设置了定时时间t调用schedule方法的时候,任务加入到DelayedWorkQueue当中等待着任务被定时执行,由上面的流程可知这里有一个不一样的地方就是无论如何线程都会被加入到工作队列中。当任务到期需要被执行的时候,从线程池中获取线程执行任务,所以这里有一个问题,如果当任务比较多的时候,可能会导致执行任务的精度不准确,因为当核心线程数被消耗完之后需要创建新线程,这里创建新线程会影响精度。随意当任务比较多的时候可以把核心线程数配置大一点来减少精度问题。
4、newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
这里ThreadPoolExecutor传入的corePoolSize和maximumPoolSize都为1,说明这只能创建一个线程来执行任务,当有任务在执行时,再来的任务加入到LinkedBlockingQueue中阻塞等待,这里还是比较简单的。
看看后面,在创建ThreadPoolExecutor之后,又被FinalizableDelegatedExecutorService包装了一层,那么这个FinalizableDelegatedExecutorService是做什么用的,点进去看看
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
我们看到,FinalizableDelegatedExecutorService继承了DelegatedExecutorService只重写了一个方法finalize()。
这样可以保证线程池被被垃圾回收的时候执行shutdown操作,避免了人为忘记。
再看看DelegatedExecutorService类,重点看看方法
static class DelegatedExecutorService extends AbstractExecutorService {
private final ExecutorService e;
DelegatedExecutorService(ExecutorService executor) { e = executor; }
public void execute(Runnable command) { e.execute(command); }
public void shutdown() { e.shutdown(); }
public List<Runnable> shutdownNow() { return e.shutdownNow(); }
public boolean isShutdown() { return e.isShutdown(); }
public boolean isTerminated() { return e.isTerminated(); }
public boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException {
return e.awaitTermination(timeout, unit);
}
public Future<?> submit(Runnable task) {
return e.submit(task);
}
public <T> Future<T> submit(Callable<T> task) {
return e.submit(task);
}
public <T> Future<T> submit(Runnable task, T result) {
return e.submit(task, result);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException {
return e.invokeAll(tasks);
}
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
return e.invokeAll(tasks, timeout, unit);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException {
return e.invokeAny(tasks);
}
public <T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException {
return e.invokeAny(tasks, timeout, unit);
}
}
再简单的看下ThreadPoolExecutor提供的方法
可以发现,DelegatedExecutorService把ThreadLocalExecutor进行了包装并且删除了其中的一些方法。
好了我们就分析到这,如果又不正确的地方欢迎大家指出,谢谢大家!