线程池有很多知识点,从源码出发更脚踏实地,此外,源码注释比起网上的讲解更详尽、表述更准确,所以有必要回顾一期源码:
来了一个任务,如果线程个数没有达到核心线程数,就会创建线程来执行任务;如果达到核心线程数,就会把任务放到任务队列中;如果任务队列满了,线程个数没有达到最大线程数,就会创建线程来执行任务;如果达到最大线程数了,就会执行拒绝策略。
线程池的目标是线程复用。线程执行第一个任务后,在Runnable里循环获取任务。线程个数大于核心线程数,线程是空闲的,超过保活时间,就会退出循环。
除了线程达到最大线程数并且任务队列满,线程状态是关闭、停止、整理、终止,也会执行拒绝策略。关闭状态下不能接收新任务,会继续处理旧任务。停止状态下不能接收新任务,中断正在执行的任务。关闭和停止状态下清空任务后,就会成为整理状态。整理状态下执行ThreadPoolExecutor的terminated方法,就会成为终止状态。
拒绝策略就是RejectedExecutionHandler的rejectedExecution方法。
submit(callable)时,会将callable传给futureTask,执行execute(futureTask)。FutureTask实现了Runnable接口。FutureTask的run方法里会调Callable的call方法。
如果是CPU密集型,核心线程数应该是CPU核心数,最大线程数可以是核心线程数+1,避免线程频繁切换带来的开销。如果是IO密集型,核心线程数可以是CPU核心数的两倍,可以用公式 (线程保活时间+线程CPU时间)/线程CPU时间*CPU核心数。CPU密集型是指逻辑运算占比大。IO密集型是指IO占比大。
public ThreadPoolExecutor(int corePoolSize, //核心线程数量
int maximumPoolSize, //最大线程数
long keepAliveTime, //空闲线程的存活时间,超过空闲时间上限时线程就结束了,默认是针对线程数大于最大线程数时,allowCoreThreadTimeOut(true)时对所有线程有效
TimeUnit unit, //存活时间单位
BlockingQueue<Runnable> workQueue, //保存执行任务的队列,即任务队列
ThreadFactory threadFactory,//创建新线程使用的工厂
RejectedExecutionHandler handler //当任务无法执行的时候的处理方式)
JDK提供了几种,但不推荐直接使用,应该按照实际需求设置参数。阿里Java手册也是这样要求的。
FixedThreadPool:
核心线程数等于最大线程数,保活时间为0,队列为无界队列(队列大小Integer.MAX_VALUE,一般用不到大小上限,所以习惯上称无界队列)。
线程池中的线程数上限固定,采用无界队列。适合任务量比较固定,任务比较耗时的场景。
/ * *
*创建重用固定数量线程的线程池
*操作一个共享的无界队列,使用提供的
* ThreadFactory在需要时创建新线程。在任何时候,
*大多数{@code nThreads}线程将被积极处理
*任务。当所有线程都被提交时,是否有其他任务被提交
*活动时,它们将在队列中等待,直到有一个线程是活动的
*可用。如果任何线程在运行期间由于失败而终止
*在关闭之前执行,一个新的将取代它,如果
*需要执行后续任务。池中的线程会
*存在,直到显式{@link ExecutorService#shutdown
*关闭}。
*
线程池中的线程数
创建新线程时要使用的线程工厂
返回新创建的线程池
* @抛出NullPointerException,如果threadFactory是空的
* @抛出IllegalArgumentException如果{@code nThreads <= 0}
* /
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
SingleThreadExecutor:
最多有一个线程, 采用无界队列。适合任务依次执行,任何时候,只有一个任务执行的场景。
/ * *
*创建使用单个工作线程操作的执行器
*关闭一个无界队列。(注意,如果这个单
*线程在执行之前由于失败而终止
*关机时,如果需要执行,会有一个新的替换
*后续任务。)任务被保证执行
*按顺序排列,任何活动的任务不超过一个
*。不像其他等价的
* {@code newFixedThreadPool(1)
*保证不被重新配置使用额外的线程。
*
返回新创建的单线程执行器
* /
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
/ * *
*创建使用单个工作线程操作的执行器
*关闭一个未绑定队列,并使用所提供的线程工厂来
*在需要时创建一个新线程。不同于其他
*等效的{@code newFixedThreadPool(1, threadFactory)
*返回的执行器保证不会被重新配置使用
*额外的线程。
*
* @param threadFactory创建新线程时要使用的工厂
*线程
*
返回新创建的单线程执行器
* @抛出NullPointerException,如果threadFactory是空的
* /
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
CachedThreadPool:
线程最多Integer.MAX_VALUE个,采用同步队列。所有线程超过60秒空闲就结束。使用同步队列,任何时候只能容纳一个任务,队列里的任务不被移除,下一个任务不能被添加。适合执行许多短期任务的场景。
/ * *
*创建一个线程池,根据需要创建新的线程,但是
*将重用之前构建的线程
*可用。这些池通常会提高性能
执行许多短期的异步任务的程序。
*调用{@code execute}将重用之前构造的
*线程(如果可用)。如果没有可用的线程,则使用新线程
*线程将被创建并添加到池中。线程,
*未使用60秒将终止并删除
*缓存。因此,一个足够长时间保持空闲的池将会
*不消耗任何资源。注意,有类似的池
*属性,但不同的细节(例如,超时参数)
*可以使用{@link ThreadPoolExecutor}构造函数创建。
*
返回新创建的线程池
* /
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
/ * *
*创建一个线程池,根据需要创建新的线程,但是
*将重用之前构建的线程
*可用,并使用所提供的
* ThreadFactory在需要时创建新线程。
创建新线程时要使用的线程工厂
返回新创建的线程池
* @抛出NullPointerException,如果threadFactory是空的
* /
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
ScheduledThreadPool:
/ * *
*创建一个线程池,可以调度命令在一个线程后运行
*给定延迟,或定期执行。
* @param corePoolSize保存在池中的线程数,
*即使他们空闲
返回一个新创建的计划线程池
* @抛出IllegalArgumentException如果{@code corePoolSize < 0}
* /
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
/ * *
*创建一个线程池,可以调度命令在一个线程后运行
*给定延迟,或定期执行。
* @param corePoolSize保存在池中的线程数,
*即使他们空闲
返回一个新创建的计划线程池
* @抛出IllegalArgumentException如果{@code corePoolSize < 0}
* /
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
WorkStealingPool:
/ * *
*创建一个线程池,维护足够的线程来支持
*给定的并行度级别,可以使用多个队列来实现
*减少争用。平行度级别对应于
*积极参与或可用的最大线程数
参与任务处理。线程的实际数量可能
*动态增长和收缩。偷工减料的人不会
*保证提交任务的顺序
*执行。
*
目标并行度级别
返回新创建的线程池
* @抛出IllegalArgumentException如果{@code parallelism <= 0}
* @since 1.8
* /
public static ExecutorService newWorkStealingPool(int parallelism) {
return new ForkJoinPool
(parallelism,
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
/ * *
*创建一个工作窃取线程池使用所有
* {@link Runtime#availableProcessors available processors}
*作为其目标并行度级别。
返回新创建的线程池
* @see # newWorkStealingPool (int)
* @since 1.8
* /
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
核心源码:
execute(Runnable command)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/ *
*分3个步骤进行:
*
* 1。如果运行的线程少于corePoolSize,请尝试
*用给定的命令作为第一个线程启动一个新线程
*任务。对addWorker的调用会自动检查runState和
* workerCount,这样可以防止虚假警报的增加
*线程时,它不应该,通过返回false。
*
* 2。如果任务可以成功排队,那么我们仍然需要
*来再次检查我们是否应该添加一个线程
*(因为在上次检查后已有的已经死亡)或那样
*自从进入此方法后池就关闭了。所以我们
*重新检查状态,如有必要回滚队列
*停止,或启动一个新线程,如果没有线程。
*
* 3。如果我们不能将任务放入队列,那么我们尝试添加一个新的
*线程。如果它失败了,我们知道我们被关闭或饱和了
*因此拒绝这项任务。
* /
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);
}
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
t.start(); // 开启线程
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
线程包装在Worker中:
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) { // 第一个任务不为空或者从队列中拿到的任务不为空
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
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);
}
}
获取队列中的任务:
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)) // 线程个数大于最大的池大小(最大线程数)或者线程保活时间(空闲时间)到了,就返回null任务,会导致退出上一层方法的循环,线程结束
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
线程池状态转换:
/ * *
*主池控制状态ctl是一个原子整数封装,前3位表示线程池状态,后29位表示线程池的线程数量
*两个概念领域
workerCount,表示线程的有效数量
* runState,指示是否运行、关闭等
*
为了将它们打包成一个int,我们将workerCount限制为
*(2^29)-1(大约5亿个)线程,而不是(2^31)-1 (2
*亿)其他可表示的。如果这是一个问题
*将来,变量可以改为AtomicLong,
*和shift/掩模常数调整如下。但直到需要
如果使用int,这段代码会更快、更简单一些。
*
工人总数是已被解雇的工人人数
*允许开始,不允许停止。价值可能是
*暂时不同于活动线程的实际数量,
*例如,当线程工厂无法创建线程时
*询问,退出线程仍在执行
*终止前簿记。用户可见的池大小为
*报告当前工人的大小设置。
*
* runState提供主生命周期控制,取值:
*
*RUNNING:接受新任务并处理已排队的任务
*SHUTDOWN:不接受新任务,但处理已排队的任务
*STOP:不接受新任务,不处理排队的任务,
*和中断正在进行的任务
*TIDYING:所有任务已终止,workerCount为零,
线程转换到状态整理
*将运行terminated()钩子方法
* TERMINATED: TERMINATED()已经完成
*
*这些值之间的数字顺序很重要
*要求比较。运行状态单调递增
*时间,但不需要击中每个状态。转换:
*
*RUNNING -> SHUTDOWN
* 对shutdown()的调用,可能在finalize()中隐式调用
*(RUNNING or SHUTDOWN) -> STOP
* 关于调用shutdownNow()
*SHUTDOWN -> TIDYING
* 当队列和池都为空时
*STOP -> TIDYING
* 当池是空的
*TIDYING -> TERMINATED
* 当terminate()钩子方法完成时
*
*线程等待awaitTermination()将返回当
*状态到达终止。
*
*检测从关机到整理的过渡更少
*比你想的要简单,因为队列可能会变成
*空后非空,反之亦然,关机状态,但
只有在看到它是空的时候,我们才能终止
workerCount为0(有时需要重新检查——请参阅
*以下)。
* /
// runState is stored in the high-order bits
private static final int RUNNING = -1 << COUNT_BITS;
private static final int SHUTDOWN = 0 << COUNT_BITS;
private static final int STOP = 1 << COUNT_BITS;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
RUNNING->SHUTDOWN->TIDYING->TERMINATED
RUNNING->STOP->TIDYING->TERMINATED
shutdown:
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN); // 转换为SHUTDOWN状态
interruptIdleWorkers(); // 中断闲置线程
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
中断空闲线程:
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
if (!t.isInterrupted() && w.tryLock()) { // 如果线程未被中断
try {
t.interrupt(); // 中断线程
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
shutdownNow():
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP); // 转换为STOP状态
interruptWorkers(); // 中断所有线程
tasks = drainQueue(); // 清空任务队列,获得未执行的任务列表
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks; // 返回未执行的任务列表
}
private List<Runnable> drainQueue() {
BlockingQueue<Runnable> q = workQueue;
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
return taskList;
}
/ * *
*转换到终止状态,如果(SHUTDOWN和池
*和队列空)或(STOP和池空)。如果否则
*有资格终止,但workerCount是非零,中断一个
*闲置工人,以确保关闭信号传播。这
方法必须在任何可能发生的操作之后调用
*可能的终止——减少员工数量或取消任务
*从队列关闭时。该方法是非私有的
*允许访问ScheduledThreadPoolExecutor。
* /
final void tryTerminate() {
for (;;) {
int c = ctl.get();
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE);
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { // 转换为TIDYING状态
try {
terminated(); // 结束方法
} finally {
ctl.set(ctlOf(TERMINATED, 0)); // 转换为TERMINATED状态
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
拒绝策略:
final void reject(Runnable command) {
handler.rejectedExecution(command, this);
}
默认拒绝策略:
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
线程池大小设置: