核心的几个线程池内部都是使用ThreadPoolExecutor实现的。
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads,
nThreads,
OL,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor(){
return new FinalizableDelegatedExecutorService(
new ThreadPoolExecutor(1,
1,
OL,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool(){
return new ThreadPoolExecutor(0,
Integer.MAX_VALUE,
60L,
TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize, //指定线程池中的线程数量
int maximumPoolSize, //指定线程池中最大的线程数量
long keeepAliveTime, //当线程数量超过了corePoolSize,多余的空闲线程的存活时间
TimeUnit unit, //keepAliveTime的时间
BlockingQueue<Runnable> workQueue, //任务队列,被提交但尚未被执行的任务
ThreadFactory threadFactory, //线程工厂,用于创建线程,一般默认即可
RejectedExecutionHandler handler) //拒绝策略,当任务太多来不及处理,如何拒绝服务
workQueue指被提交但未执行的任务队列,它是一个BlockingQueue接口的对象,仅用于存放Runnable对象。
ThreadPoolExecutor的构造函数可以使用以下几种BlockingQueue。
直接提交的队列:SynchronousQueue。没有容量,总是将新任务提交给线程执行,即一提交任务就马上尝试要执行。如果没有空闲的线程,则尝试创建新的线程。如果线程数量已经达到了最大值,,则执行拒绝策略。
因此,通常要设置很大的maximumPoolSize值,否则很容易执行拒绝策略。
有界的任务队列:ArrayBlockingQueue。它的构造函数必须带一个容量参数,表示该队列的最大容量。
public ArrayBlockingQueue(int capacity)
当有新的任务需要执行,如果线程池的实际线程数小于corePoolSize,则会优先创建线程,若大于corePoolSize,则会将新任务加入等待队列。若队列已满,无法加入,则在总线程数不大于maximumPoolSize的前提下,创建新的线程执行任务。
若大于maximumPoolSzie,则执行拒绝策略。
有界队列仅当在任务队列装满时,才可能将线程数提高到corePoolSize以上。除非系统繁忙,否则确保核心线程数维持在corePoolSzie。
无界任务队列:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。
当有新的任务到来时,系统的线程数目小于corePoolSzie时,线程池就会生成新的线程执行任务,但线程数达到corePoolSzie后,就不会继续增加。
若后续仍有新的任务加入,而无空闲的线程时,则任务加入队列。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直至耗尽系统内存。
优先任务队列:PriorityBlockingQueue。是一个特殊的无界队列。之前的队列都是按照先进先出的原则进行的。而PriorityBlockingQueue总是确保高优先级的任务先执行。
newFixedThreadPool(),返回一个corePoolSize和maximumPoolSzie大小一样,并且使用LinkedBlockingQueue的线程池。对于固定线程数目的线程池不存在线程数目的变化。使用无界队列存放无法立即执行的任务,当任务提交非常频繁的时候,该队列可以迅速膨胀,从而耗尽系统资源。
newSingleThreadExecutor()是newFixedThreadPool()的退化版本。只是把线程数量设置为1。
newCachedThreadPool()返回的corePoolSize为0,maximumPoolSize无穷大。没有任务的时候,该线程池内线程,当任务被提交的时候,该线程池会使用空闲线程执行任务,没有空闲线程则提交给SynchronousQueue队列,而SynchronousQueue队列是直接提交的队列,它会是线程池创建新的线程执行任务。
当任务执行完毕,由于corePoolSzie为0,因此空闲线程会在指定时间(60s)内被回收。
//下面是ThreadPoolExecutor的核心实现
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
int c = ctl.get();
//workerCountOf()取得当前线程池的线程总数
//当小于corePoolSize,调用addWorker
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//addWorker():进入队列失败,将任务直接提交给线程池
else if (!addWorker(command, false))
reject(command); //如果当前线程数已经达到maximumPoolSize,就执行拒绝策略
}