通过构造函数分析线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
一共有7个参数,详细解释如下
- coresize
核心线程池线程数目,也可以理解为线程池基本大小 - maximumpoolsize
线程池的最大大小 - keepAliveTime
存活时间,如果一个线程不在基本线程池中,那么这个线程在经过AliveTime时间之后会被回收线程资源 - TimeUtil
时间的单位 - BlockingQueue
阻塞队列,存储runnable任务 - ThreadFactory
线程创建工厂 - RejectedExecutionHandler
拒绝继续执行runnnable的处理器
当线程池第一次提交Runnable时
excutor.excute(new Runnable() {
public void run() {}
})
由于刚开始是没有线程的,因此我们需要创建线程,创建线程时我们需要将当前线程的数量与coreSize相比较,如果小于则直接创建线程,如果超出了这个coreSize的大小,则把任务填充到BlockingQueue阻塞队列中,当有空闲线程时再从阻塞队列中读取,这属于生产中-消费者模型,当阻塞队列已满时,将当前任务数与
maximumPoolSize比较,如果小于溢出的任务将继续创建线程,如果大于则触发RejectedExecutionHandler。整个过程中,线程的创建由ThreadFactory来完成。那么keepAliveTime又是如何处理的呢?我们注意到,在阻塞队列满之后,我们仍然是通过创建线程的方式来处理runnable的,那么,coresize的线程与maxSize的线程的区别又在哪呢?实际上,只有对于非基本线程(coreSize以内的),在keepAliveTime时间后会被回收线程资源,而基本线程数目则是再被懒加载之后就再也不会变动了。
Executors静态工厂方法创建线程池
jdk提供了以下几个方法创建几种典型的线程池,我们可以分别看看他们的构造函数转而真正理解他们的原理
- newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
用于创建固定长度的线程池
- newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
用于创建单个线程的线程池,可能会有人认为创建单个线程的线程池是没有意义的,但是实际上,由于阻塞队列的存在,因此这种模式可以提交多个runnable,并且保证他们的顺序调用。而且有时不正确的异常处理会导致线程被终结,而线程池可以确保总是存在一个线程用来执行任务。
- newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
基于缓存的线程池,由参数我们可知,coresize为0,而maxSize为最大值,因此此类线程池对于提交的任务,总是会创建线程,而线程在空闲时会被回收
- ScheduledThreadPoolExecutor
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
定时任务执行的线程池,原理是使用了DelayedWorkQueue
拒绝执行策略
- 拒绝执行并抛出异常 (默认的执行策略)
- 直接使用当前执行excute方法线程执行这个任务
- 直接丢弃当前队列中最老的任务
- 直接抛弃而不做处理
其他策略
- 写入日志用于以后再处理