线程池简介
线程池的优点:提高性能,提高线程的利用率,可控制线程的最大并发数,方便线程管理。
线程池的核心类是 ThreadPoolExecutor, 可通过 Executors 中的几个方法创建线程池,分别是:
- CachedThreadPool
核心线程数为 0, 线程数量无上限 - FixedThreadPool
核心线程数由使用者自己指定,无非核心线程,线程空闲时不会被回收,除非线程池被关闭 - ScheduledThreadPool
指定数量的核心线程数,线程数无上限,非核心线程空闲时会被回收 - SingleThreadExecutor
核心线程为 1, 无非核心线程,线程空闲时不会被回收。意义在于使得任务都放在同一线程处理,不必考虑线程同步问题。
线程池构造方法参数介绍
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// ...
}
上面是线程池的构造方法,分析一下创建线程池的七个参数的含义:
- corePoolSize
核心线程数,在空闲时不会被回收,在线程池关闭后才会被回收(allowCoreThreadTimeOut 被设为 true 时会闲置 keepAliveTime 后回收)。 - maximunPoolSize
线程数上限,可利用该参数控制线程最大并发数,当运行的线程超过这个数量时会被阻塞。 - keepAliveTime
非核心线程的闲置时长,超过这个时长就会被回收,当 allowCoreThreadTimeOut 被设为 true 后,这个时长也同样作用于核心线程。 - unit
keepAliveTime 的时间单位。 - workQueue
线程池中的任务队列。 - threadFactory
用于创建线程,它只有一个 newThread(Runnable r) 方法。 - handler
如果执行(添加)任务失败,则会执行 handler.rejectedExecution(command, this); 这个方法在需要处理拒绝后的操作时由我们自己实现来处理。
线程池执行过程
线程池创建过后执行会调用 ThreadPoolExecutor 的 execute(Runnable) 方法来执行线程,步骤如下:
- 如果当前线程数小于核心线程,则创建新的线程保存到核心线程并执行;
- 否则,核心线程数已满,则插入到任务队列中等待
- 插入到任务队列失败,则启动非核心线程来执行
- 启动非核心线程失败,则 reject.
源码解析
那么线程池传过去的 Runnable 是怎么运行的,任务队列是怎么排队的,核心线程又是怎么创建和销毁的,从源码中找答案。
Runnabla 是怎么运行的
首先调用 execute 方法,
public void execute(Runnable command) {
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);
}
// 添加到非核心线程,失败则 reject
else if (!addWorker(command, false))
reject(command);
}
接下来看 addWorker 方法,执行 Runnable 的方法
private boolean addWorker(Runnable firstTask, boolean core) {
// ... 一些校验条件
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 创建 Worker, 内部赋值了 firstTask 并创建了线程,通过 threadFactory.new Thread(worker)
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int rs = runStateOf(ctl.get());
// 对运行状态做校验
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
// ...
// 把创建的 worker 对象保存下来
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
// 成功添加,则开始执行,t.start 会调用 worker.run 方法,因为 newThread 时传的 Runnable 对象是 worker
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
上面分析到 t.start 调的是 worker.run, worker.run 实际执行的是 runWorker(), 下面接着来看这个方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; // 这里的 task 就是 execute 方法传的 Runnable 对象
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 循环取 task 执行,第一次 task 不是 null, 第一次循环执行完,下一次就会取 getTask
while (task != null || (task = getTask()) != null) {
w.lock();
// ... 校验
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 这里就是我们调用 execute 方法时实际执行的地方了
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
上面就看到 Runnable 实际执行的部分了,Runnable 怎么执行的分析就到此结束。
任务队列是怎么排队的呢
上面代码中看到一个 for 循环不断 getTask 就是获取 Runnable 的过程,下面看 getTask 方法
private Runnable getTask() {
boolean timedOut = false; // Did the last poll() time out?
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 取任务就是在这里,从 workQueue 中取任务,是通过 workQueue.offer(runnable) 添加的
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
核心线程池又是怎么创建和销毁的
创建核心线程就是在 new Worker 时创建的线程, runWorker方法中 while 循环 getTask 获取 Runnable, getTask 获取不到 Runnable 时会调用 processWorkerExit 来销毁线程,getTask 获取 Runnalble 是通过 workQueue.poll() / workQueue.take().
当创建线程池时设置空闲一定时间就销毁时,就调用 workQueue.poll(), 下面看一下 poll 方法的源码:
public E poll(long timeout, TimeUnit unit) throws InterruptedException {
E x = null;
int c = -1;
// timeout 就是线程闲置这个时间后就需要销毁的
long nanos = unit.toNanos(timeout);
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
while (count.get() == 0) {
if (nanos <= 0)
return null;
// 如果队列为空,线程就等待 timeout 时间
nanos = notEmpty.awaitNanos(nanos);
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
if (c == capacity)
signalNotFull();
// 所以如果这里在等待 timeout 时间后还没有任务,返回到的就是空
return x;
}
上面 poll 方法中返回为空时,上面 runWorker 方法中的 while 循环就结束了,那么久会执行 processWorkerExit 方法来中断这个线程,于是就结束执行了这个线程。
设置 allowCoreThreadTimeOut 为 false 时就调用 workQueue.take(), workQueue.take() 方法看一下代码:
public E take() throws InterruptedException {
E x;
int c = -1;
final AtomicInteger count = this.count;
final ReentrantLock takeLock = this.takeLock;
takeLock.lockInterruptibly();
try {
// 当队列为空时,线程会一直处于等待状态
while (count.get() == 0) {
notEmpty.await();
}
x = dequeue();
c = count.getAndDecrement();
if (c > 1)
notEmpty.signal();
} finally {
takeLock.unlock();
}
return x;
}
所以如果核心线程是不可销毁的,那么会调用 workQueue.take(), 在没有任务时线程就会处于等待状态,直到有任务再返回,所以这样情况下核心线程是不会被销毁的。