线程池的优点
- 1、线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用。
- 2、可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃。
线程池类图结构:
- Executor接口:其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command),
- ExecutorService接口:是一个比Executor使用更广泛的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
- AbstractExecutorService类:ExecutorService执行方法的默认实现
- ScheduledExecutorService接口:一个可定时调度任务的接口
- ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池
- ThreadPoolExecutor:线程池,通过调用Executors的静态工厂方法来创建线程池并返回一个ExecutorService对象
Executors使用静态工厂方法创建的线程池,对外返回ExecutorService实例,而实际创建的对象使用的是ThreadPoolExecutor类的实例。
Executors中创建线程池的静态方法:
- static ExecutorService newCachedThreadPool() 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们。
- static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) 创建一个可根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们,并在需要时使用提供的 ThreadFactory 创建新线程。
- static ExecutorService newFixedThreadPool(int nThreads) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。
- static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) 创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程,在需要时使用提供的 ThreadFactory 创建新线程。
- static ExecutorService newSingleThreadExecutor() 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程。
- static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) 创建一个使用单个 worker 线程的 Executor,以无界队列方式来运行该线程,并在需要时使用提供的 ThreadFactory 创建新线程。
- static ExecutorService unconfigurableExecutorService(ExecutorService executor) 返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
- static ScheduledExecutorService newScheduledThreadPool(int corePoolSize, ThreadFactory threadFactory) 创建一个线程池,它可安排在给定延迟后运行命令或者定期地执行。
- static ScheduledExecutorService newSingleThreadScheduledExecutor() 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
- static ScheduledExecutorService newSingleThreadScheduledExecutor(ThreadFactory threadFactory) 创建一个单线程执行程序,它可安排在给定延迟后运行命令或者定期地执行。
- static ScheduledExecutorService unconfigurableScheduledExecutorService(ScheduledExecutorService executor) 返回一个将所有已定义的 ExecutorService 方法委托给指定执行程序的对象,但是使用强制转换可能无法访问其他方法。
以newFixedThreadPool(int nThreads)为例分析源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
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);
}
ThreadPoolExecutor中一共有四个构造方法,其中三个都是调用this(....)构造创建实例,也就是下面的构造:
//this(....)调用的是下面的构造方法
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
构造函数中参数解析:
- int corePoolSize: 核心线程数,若运行的线程数小于corePoolSize,则创建新线程来执行任务。
- int maximumPoolSize:允许创建的最大线程数。 若corePoolSize< 运行线程数 < maximumPoolSize,则仅在队列满时创建新线程;若corePoolSize= 运行线程数 =maximumPoolSize,创建固定大小的线程池。
- long keepAliveTime:当线程数多于corePoolSize,多出的线程空闲时间超过keepAliveTime时将被终止。
- TimeUnit unit:keepAliveTime参数的时间单位,微秒、毫秒、秒等
- BlockingQueue<Runnable> workQueue:保存任务的阻塞队列,与线程池大小有关。当运行的线程数少于corePoolSize,在有新线程任务时直接创建新线程来执行任务而无需进入队列;当运行线程数 >= corePoolSize,新的线程任务会进入队列,不创建线程;当队列满时,新的线程任务进入会创建新线程。
- ThreadFactory threadFactory:使用ThreadFactory创建线程,默认使用deaufltThreadFactory创建线程。
- RejectedExecutionHandler handler:定义处理被拒绝任务的策略,使用默认ThreadPoolExecutor.AbortPolicy,任务被拒绝时将抛出RejectExecutorException。
线程池调用:
创建好线程池之后,下面要用线程池执行任务。调用线程池执行任务的方法有两类:execute 和 submit 。execute方法在线程池的超类Executor中定义,在ThreadPoolExecutor中实现;submit方法是在ExecutorService中定义,在AbstractExecutorService中实现。
executor()方法:
/**
* 在将来某个时候执行给定的任务。
* 如果无法提交任务供执行,要么是因为此执行器已关闭,要么是因为其容量已达到,
* 则由当前{@code RejectedExecutionHandler}处理该任务。
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//如果运行线程数小于核心线程数,创建新的线程执行任务
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);
}
//线程池不处于运行状态且加入队列失败,则创建线程(创建的是非核心线程)
else if (!addWorker(command, false))
//创建线程失败,则采取阻塞处理的方式
reject(command);
}
execute执行线程任务,主要三个步骤:
- 活动线程小于corePoolSize的时候创建新的线程;
- 活动线程大于corePoolSize时都是先加入到任务队列当中;
- 任务队列满了再去启动新的线程,如果线程数达到最大值就拒绝任务。
submit方法:
AbstractExecutorService抽象类中有三个submit重载方法:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
submit方法,将线程执行记过存放到Future中。
方法中涉及到Future接口、RunnableFuture接口和newTaskFor()方法:
protected <T> RunnableFuture<T> newTaskFor(Runnable runnable, T value) {
return new FutureTask<T>(runnable, value);
}
这里又出现了一个类FutureTask,FutureTask是RunnableFuture的实现类,而RunnableFuture接口又是Future和Runnable的子类。
FutureTask是一个包装类,API中对FutureTask的描述:“一个可取消的异步计算。提供了启动和取消计算、查询计算是否完成以及检索计算结果的方法。只有在计算完成后才可检索结果;如果计算尚未完成,{@code get}方法将阻塞。一旦计算完成,就不能重新启动或取消计算”
关闭线程池:
关闭线程池有两个方法:shutdown()和shutdownNow()
- shutdown方法等待提交的任务执行完成并不再接受新任务,在完成全部提交的任务后关闭
- shutdownNow方法将强制终止所有运行中的任务并不再允许提交新任务
/**
* 将正在执行的任务完成,但是不会接受新的任务。任务执行完毕后关闭线程池
*
*/
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN);
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
/**
* 尝试停止所有正在积极执行的任务,停止处理等待的任务,并返回等待执行的任务列表。
* 从该方法返回时,将从任务队列中删除这些任务。
*/
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
public boolean isShutdown() {
return ! isRunning(ctl.get());
}