前言
本篇主要介绍线程池的源码。
Java中内置的线程池
Java中内置的线程池有两种:
- ThreadPoolExecutor
- Executor
阿里的规范中强烈不推荐使用Executors这个工具类线程池,但是我们还是需要讲解他的使用、优缺点以及为什么不选择该工具类。
我们先讲一讲线程池相关的重要接口。
Executor接口
public interface Executor {
/**
* Executes the given command at some time in the future. The command
* may execute in a new thread, in a pooled thread, or in the calling
* thread, at the discretion of the {@code Executor} implementation.
*
* @param command the runnable task
* @throws RejectedExecutionException if this task cannot be
* accepted for execution
* @throws NullPointerException if command is null
*/
void execute(Runnable command);
}
我们可以看到,Executor 接口中只有一个方法 execute(Runable command); 。我们可以很清楚的看到注释中的意思是
在未来执行给定的命令,这个命令可能在一个新线程执行,也有可能在一个线程池线程执行,或者在calling线程执行。
也就是说他就是会将我们放入该函数的形参执行的方法。
ExecutorService接口
ExecutorService接口继承了Executor接口,并且添加了很多新方法,我们一起来看看有哪些。
public interface ExecutorService extends Executor {
//关闭线程
void shutdown();
//关闭并且获得所有还未完成的线程
List<Runnable> shutdownNow();
//判断是否关闭
boolean isShutdown();
//判断
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
//提交任务
<T> Future<T> submit(Callable<T> task);
//提交任务
<T> Future<T> submit(Runnable task, T result);
//提交任务
Future<?> submit(Runnable task);
//将容器中的线程全部提交并且返回
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//同上
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
throws InterruptedException, ExecutionException;
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException, ExecutionException, TimeoutException;
}
ThreadPoolExecutor类详解
它是Executor的一个实现类,同时也是最常用的线程池实现类。
我们先看看该类的四个构造方法。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
我们可以看到,其实前三个构造方法,都是调用了第四个构造方法,并没有具体的实现,那我们重点观察第四个构造方法。
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;
}
老规矩,我们先判断是否有非法或者越界的情况发生,若是有那么便抛出异常。
然后我们判断是否有空的情况,若是有也抛出异常。
其中**System.getSecurityManager()**是java内置的安全管理器,主要是代码权限控制,避免恶意代码损害机器,读者有兴趣可以自行去了解。
我们可以看到最核心的参数是以下几个,分别阐述其功能。
- corePoolSize :核心线程池大小,其意义是若是创建那么就不会销毁的线程的数量。
- maximumPoolSize :最大线程池大小,其意义是最多线程数目大小。
- workQueue :一个阻塞队列,用于存放排队中的线程。
- keepAliveTime :线程存活时间。
- threadFactory :线程创建工厂。
- handler :拒绝策略。
我们先来看看线程池中最常用的方法execute(Runnable command)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
*
* 1. If fewer than corePoolSize threads are running, try to
* start a new thread with the given command as its first
* task. The call to addWorker atomically checks runState and
* workerCount, and so prevents false alarms that would add
* threads when it shouldn't, by returning false.
*
* 2. If a task can be successfully queued, then we still need
* to double-check whether we should have added a thread
* (because existing ones died since last checking) or that
* the pool shut down since entry into this method. So we
* recheck state and if necessary roll back the enqueuing if
* stopped, or start a new thread if there are none.
*
* 3. If we cannot queue task, then we try to add a new
* thread. If it fails, we know we are shut down or saturated
* and so reject the task.
*/
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);
}
根据注释,这个方法要分三个步骤进行。
- 若正在运行的线程少于corePoolSize,那么尝试使用给定命令作为第一个任务启动新线程。addWorker从原子上检查运行状态和运行数目,避免并发状态下不应该添加线程的时候发出警报。
- 若一个任务成功排队,还要检查是否应该添加一个线程,重新检查状态,在有必要的时候回滚。
- 若无法将任务排队,则尝试添加一个新线程,若失败则说明已经关闭或者已经满,拒绝该任务并且调用拒绝策略。
正常只需要调用execute方法,线程池就暂时讲到这里,留待以后补充。