1.为什么我们需要了解ThreadPoolExecutor?
java的并发包为我们提供了一个Executors(java.util.concurrent)工具类,通过这个工具类我们可以很方便的创建线程池,其中:
(1).创建无界线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
(2).创建有界线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
(3).创建单一线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
上述三个创建线程池的方法均用到了ThreadPoolExecutor的构造方法(还有很多地方没有一一列出):
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
所以了解此构造方法是非常有必要的。
2.该构造方法的参数解释如下:
corePoolSize:线程池中保存的线程数,包括空闲线程,也就是核心池的大小。
maximumPoolSize:线程池允许的最大线程数。
keepAliveTime:当线程数量大于corePoolSize值时,在没有超过指定时间时不从线程池中将空闲的线程删除,
如果超过了指定的时间则会被删除。
unit:keepAliveTime的时间单位。
workQueue:执行前用于保存任务的队列。此队列仅保持由execute提交的runnable任务。
在线程的使用过程中基本遵循如下论据:
(1).如果欲执行的任务(也就是调用:executorService.execute(Runnable r)的次数)小于线程核心池的大小,那么任务并不会被放入扩展队列queue中,同时其他参数可以忽略。
(2).如果欲执行的任务大于线程核心池的大小但不大于线程池允许的最大线程数,同时使用单向链表阻塞队列(LinkedBlockingQueue)来当做workQueue的话,线程池此时将核心池能容纳的最大值剩下的任务存入队列中,此时可以忽略keepAliveTime和timeUnit这两个参数。
(3).如果欲执行的任务大于线程核心池的大小但不大于线程池允许的最大线程数,同时使用同步阻塞队列(SynchronousQueue)来当作workQueue的话,keepAlive与timeUnit这两个参数有效,但此时不会将线程池此时将核心池能容纳的最大值剩下的任务存入队列(SynchronousQueue同步阻塞队列)中而是马上创建线程将多余的任务执行,当执行完成并且超过了keepAliveTime以后将多余的任务清除。
(4).如果欲执行的任务大于线程核心池的大小同时大于线程池允许的最大线程数,同时使用单向链表阻塞队列(LinkedBlockingQueue)来当作workQueue的话,线程池此时将核心池能容纳的最大值剩下的任务存入队列中,此时可以忽略keepAliveTime、timeUnit、maximumPoolSize这三个参数。
(5).如果欲执行的任务大于线程核心池的大小同时大于线程池允许的最大线程数,同时使用同步阻塞队列(SynchronousQueue)来当作workQueue的话,此时会有maximumPoolSize个线程执行任务,同时不再处理剩下的任务,抛出异常。