- 为什么要使用线程池?
线程的创建需要开辟虚拟机栈,本地方法栈、程序计数器等线程私有的内存空间。在线程的销毁时需要回收这些系统资源,频繁的创建和销毁线程会浪费大量的系统资源。
- 如何创建线程池?
《阿里巴巴Java开发手册》中强制线程不允许使用Executors显示创建线程,而是使用TreadPoolExecutor的方式,这种创建方式会明确线程池的运行规则,避免资源耗尽的风险。
TreadPoolExecutor 一共有四个构造方法,参数最多的方法有7个参数。
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;
}
corePoolSize:核心线程数的大小,即在没有任务需要执行的时候线程池的大小,并且只有在工作队列满了的情况下才会创建超出这个数量的线程。这里需要注意的是:在刚刚创建ThreadPoolExecutor的时候,线程并不会立即启动,而是要等到有任务提交时才会启动。
maximumPoolSize:线程池中允许的最大线程数,线程池中的当前线程数目不会超过该值。如果队列中任务已满,并且当前线程个数小于maximumPoolSize,那么会创建新的线程来执行任务。
keepAliveTime:线程空闲时间,当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize
workQueue:当新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到,新任务就会被存放到队列中。
threadFactory:executor创建线程时会用到
handler:当线程数已经达到maxPoolSize,切队列已满,会拒绝新任务,有四种策略方式
- AbortPolicy 丢弃任务,抛运行时异常(默认) - CallerRunsPolicy 执行任务 - DiscardPolicy 忽视,什么都不会发生 - DiscardOldestPolicy 从队列中踢出最先进入队列(最后一个执行)的任务
当一个任务提交到线程池时,会进行如下判断。
TreadPoolExecutor创建的四种线程池:
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。