为了避免重复的创建线程,线程池的出现可以让线程进行复用。通俗来讲,当有任务提交时,就会向线程池拿一个线程,当任务完成后,并不是直接关闭线程,而是将这个线程归还给线程池供其他任务使用。
ThreadPoolExecutor:
ThreadPoolExecutor是线程池的实现类。
ThreadPoolExecutor的构造器:
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:线程池的核心池大小,在创建线程池之后,线程池默认没有任何线程。除非调用了prestartAllCoreThreads()或prestartCoreThread()方法,这两个方法是预创建线程的方法,即在没有任务到来之前会创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务提交,就会创建一个线程去执行任务,当线程池的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列中。
maximumPoolSize:线程池的最大线程数。如果阻塞队列满了,并且已创建的线程数小于最大线程数,则线程池会在创建新的线程执行。
keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,当线程池中的线程数大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,线程则会终止,直到线程池中的线程数不大于corePoolSize。如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0。
unit:参数keepAliveTime的时间单位。
workQueue:阻塞队列,用来存储等待执行的任务。阻塞队列一般有以下几种:
ArrayBlockingQueue;//数组结构的有界阻塞队列,先进先出,创建时必须指定大小
LinkedBlockingQueue;//链表结构的无界阻塞队列,先进先出
SynchronousQueue;//不存储元素的阻塞队列,每次插入操作必须等到另一个线程调用移除操作threadFactory:线程工厂,主要用来创建线程
handler:拒绝处理任务策略,有以下四种取值。
ThreadPoolExecutor.AbortFactory;//丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy;//丢弃任务,但不抛出异常
ThreadPoolExecutor.DiscardOldestPolicy;//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
ThreadPoolExecutor.CallerRunsPolicy;//由调用线程处理该任务线程池的任务处理策略
首先取得当前线程池中的线程总数,与核心池大小比较,
- 如果小于核心池大小,将通过addWorker()方法创建线程执行
- 如果大于,则提交到阻塞队列
- 如果阻塞队列已满,则把任务直接提交到线程池
- 如果当前线程数达到最大线程数,则提交失败,执行拒绝策略,否则分配线程执行。
线程池的关闭
ThreadPoolExecutor提供了两个方法关闭线程池:
shutdown();//不会立即终止线程池,等所有的任务和阻塞队列的任务都执行完才终止,但不再接受新的任务。
shutdownNow();//立即终止线程池,并尝试打断正在执行的任务,并清空阻塞队列,返回尚未执行的任务线程池的状态
volatile int runState;
static final int RUNNING = 0;//运行状态
static final int SHUTDOWN = 1;//关闭状态
static final int STOP = 2;//停止
static final int TERMINATED = 3;//终止- 创建线程池后,初始状态为RUNNING
- 如果调用了shutdown(),则线程池处于SHUTDOWN状态,此时线程池不再接受新的任务,它会等所有任务执行完毕,最后终止。
- 如果调用了shutdownNow(),线程池处于STOP状态,此时线程池不能接受新的任务,并且去尝试终止正在执行的任务,返回没有执行的任务列表。
- 当线程池处于SHUTDOWN或STOP状态,并且所以工作线程已经销毁,阻塞队列已经清空或执行完之后,线程池被设置为TERMINATED状态。
常见的四种线程池
FixedThreadPool:固定大小的线程池
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}固定大小的线程池,该线程池corePoolSize和maximumPoolSize相等,阻塞队列为LinkedBlockingQueue。固定大小的线程池,不存在线程数量的变化。
SingleThreadExecutor:单线程线程池
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}单线程线程池,只有一个线程的线程池,阻塞队列用LinkedBlockingQueue,若有多余的任务提交到线程池中,则会被暂存到阻塞队列,等空闲时再去执行,按照先进先出的顺序执行。该线程池可保证任务按照被加入顺序执行。
CachedThreadPool:缓存线程池
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}缓存线程池,缓存的线程默认存活60秒。线程池的corePoolSize大小为0,最大线程数为Integer.MAX_VALUE,阻塞队列使用的是SynchronousQueue,是一个直接提交的阻塞队列,它总会迫使线程池增加新的线程去执行任务。当线程的空间时间超过60s,则工作线程将会终止被回收。当提交新任务时,如果没有空闲线程,则创建新线程执行任务。
ScheduledThreadPool:定时线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}定时线程池,可用于周期性地执行任务,通常用于周期性的同步数据。