Executor框架
1、概述
Executor框架包括:线程池,Executor,Executors,ExecutorService,CompletionService,Future,Callable 等。
2、类图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HQDeCTjP-1639989014135)(/分享/1.png)]
3、核心接口、类分析
3.1 Executor 接口
Executor:接口,执行器,将任务的提交和执行解耦,其定义了一个接收Runnable对象的方法executor,其方法签名为executor(Runnable command)
public interface Executor {
//执行一个任务,任务被封装在Runnable接口
void execute(Runnable command);
}
3.2 ExecutorService 接口
Executor的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法,ExecutorService 的生命周期包括三种状态:运行、关闭、终止。创建后便进入运行状态,当调用了 shutdown()方法时,便进入关闭状态,此时意味着 ExecutorService 不再接受新的任务,但它还在执行已经提交了的任务,当素有已经提交了的任务执行完后,便到达终止状态。如果不调用 shutdown()方法,ExecutorService 会一直处在运行状态,不断接收新的任务,执行新的任务,服务器端一般不需要关闭它,保持一直运行即可。
public interface ExecutorService extends Executor {
//启动有序关闭,在此过程中执行先前提交的任务,但不接受新的任务。如果已经关闭,调用不会产生额外的影响
void shutdown();
//尝试停止所有正在执行的任务,停止对正在等待的任务的处理,并返回正在等待执行的任务的列表。
List<Runnable> shutdownNow();
//如果这个执行器已经被关闭,返回true。
boolean isShutdown();
//如果关闭后所有任务都已完成,则返回true。注意,除非首先调用shutdown或shutdownNow,否则isTerminated永远不会为真
boolean isTerminated();
//阻塞,直到关闭后所有任务都完成执行请求,或超时发生,或当前线程是中断,以先发生者为准。
boolean awaitTermination(long timeout, TimeUnit unit)
throws InterruptedException;
// 提交一个返回值的任务执行,并返回一个Future,表示该任务的未决结果。Future的get方法将在任务成功完成后返回任务的结果。 如果你想立即阻塞等待任务,你可以使用result = exec.submit(aCallable).get(); 注意:executor类包含一组方法,可以将一些其他常见的闭包类对象转换为Callable形式,例如java.security.PrivilegedAction,以便提交。
<T> Future<T> submit(Callable<T> task);
//提交一个Runnable任务执行,并返回一个表示该任务的Future。Future的get方法将在成功完成时返回给定的结果。
<T> Future<T> submit(Runnable task, T result);
//提交一个Runnable任务执行,并返回一个表示该任务的Future。Future的get方法将在成功完成时返回null。
Future<?> submit(Runnable task);
// 执行给定的任务,当所有任务完成时返回一个包含其状态和结果的Futures列表。的未来。对于返回列表的每个元素,isDone为true。请注意,已完成的任务可以正常终止,也可以通过抛出异常终止。如果在执行此操作时修改了给定集合,则此方法的结果是未定义的。
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
//执行给定的任务,在所有任务完成或超时到期时返回一个包含其状态和结果的Futures列表(以先发生的为准)。的未来。对于返回列表的每个元素,isDone为true。返回时,未完成的任务被取消。请注意,已完成的任务可以正常终止,也可以通过抛出异常终止。如果在执行此操作时修改了给定集合,则此方法的结果是未定义的
<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;
}
3.3 AbstractExecutorService:抽象类,ExecutorService执行方法的默认实现,未实现Executor的execute方法
3.4 ThreadPoolExecutor:Executor框架最核心的类,线程池的实现类,继承AbstractExecutorService
3.4.1 构造方法及参数说明:
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;
}
-
corePoolSize:核心线程的个数,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了下面两个方法:
- prestartCoreThread(); //初始化一个核心线程;
- prestartAllCoreThreads();//初始化所有核心线程
-
maximumPoolSize:线程池最大线程数,限定为2^29-1。
-
keepAliveTime:表示线程没有任务执行时最多保持多久时间会回收。默认只对非核心线程起作用,当调用allowCoreThreadTimeOut(true),对核心线程也起作用
-
unit:unit是参数keepAliveTime的时间单位,在TimeUnit类中有7种静态属性。
-
workQueue: 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般情况下,有几种选择
- ArrayBlockingQueue; //有界队列,基于数组的先进先出队列,此队列创建时必须指定大小;
- PriorityBlockingQueue; //一个无界的阻塞队列
- LinkedBlockingQueue; //有界队列,无参可视为无界队列,基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
- SynchronousQueue; //同步移交队列,这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
- 名义上是个队列,但底层并不维护链表也没有维护数组,在一个线程调用它的put方法时会立即将塞入的元素转交给调用take的线程,如果没有调用take的线程则put方法会阻塞
-
threadFactory:线程工厂,主要用来创建线程。
-
handler:表示当拒绝处理任务时的策略,当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略
- ThreadPoolExecutor.AbortPolicy;//丢弃任务并抛出RejectedExecutionException异常。
- ThreadPoolExecutor.DiscardPolicy;//也是丢弃任务,但是不抛出异常。
- ThreadPoolExecutor.DiscardOldestPolicy;//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
- ThreadPoolExecutor.CallerRunsPolicy;//由调用线程处理该任务
-
ScheduledExecutorService:一个可定时调度任务的接口
-
ScheduledThreadPoolExecutor:ScheduledExecutorService的实现,一个可定时调度任务的线程池
-
Executors 提供了一系列工厂方法用于创先线程池,返回的线程池都实现了 ExecutorService 接口。
3.4.2 核心成员变量分析
- 线程池中设计非常巧妙的一个地方是把线程池的状态和运行的线程数量用一个int类型进行存储。这样一来可以保持线程池状态和线程池活跃线程数量的一致性。因为AtomicInteger是线程安全的。
- workerCount:线程池中当前活动的线程数量,占据ctl的低29位;
- runState:线程池运行状态,占据ctl的高3位,有RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED五种状态
- 为了将线程池的状态和线程池中的工作线程的数量放到一个int里面进行管理。这里利用了二进制数据进行位运算。其中int类型有4个字节,一个字节8位。总共有32位。其中高的3位表示线程的状态。低29位代表线程的数量。其中32位中,高三位代表的是状态:
- 111 > RUNNING
- 000 > SHUTDOWN
- 001 > STOP
- 010 > TIDYING
- 110 > TERMINATED
低29位代表线程的数量。所以最大的线程数为 2^29 -1 = 536870911
// 记录线程池状态和线程数量(总共32位,前三位表示线程池状态,后29位表示线程数量),保证线程安全性
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// int 字节32位,COUNT_BITS代表的是29位
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程的最大容量: 000 11111111111111111111111111111
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 运行状态: 111 00000000000000000000000000000
private static final int RUNNING = -1 << COUNT_BITS;
// 关闭状态: 000 00000000000000000000000000000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 停止状态: 001 00000000000000000000000000000
private static final int STOP = 1 << COUNT_BITS;
// 整理状态: 010 00000000000000000000000000000
private static final int TIDYING = 2 << COUNT_BITS;
// 终止状态: 011 00000000000000000000000000000
private static final int TERMINATED = 3 << COUNT_BITS;
/**
* 是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,2低位的9个0,
* 然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState
*/
private static int runStateOf(int c) { return c & ~CAPACITY; }
/**
* 返回当前线程的数量。其中c代表线程池的状态,即是高三位。:
* 而CAPACITY 代表的是线程的容量,即000 11111111111111111111111111111
* c & CAPACITY ,只有当都为1的时候,才为真,这样直接舍弃高位
*/
private static int workerCountOf(int c) { return c & CAPACITY; }
/**
* 传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,
* 而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值得int,
* 将runState和workerCount做或操作|处理,即用runState的高3位,workerCount的低29位填充的数字,而默认传入的
*/
private static int ctlOf(int rs, int wc) { return rs | wc; }
/**
* 是按位取反的意思,CAPACITY表示的是高位的3个0,和低位的29个1,而~CAPACITY则表示高位的3个1,2低位的9个0,
* 然后再与入参c执行按位与操作,即高3位保持原样,低29位全部设置为0,也就获取了线程池的运行状态runState
*/
private static int runStateOf(int c) { return c & ~CAPACITY; }
/**
* 返回当前线程的数量。其中c代表线程池的状态,即是高三位。:
* 而CAPACITY 代表的是线程的容量,即000 11111111111111111111111111111
* c & CAPACITY ,只有当都为1的时候,才为真,这样直接舍弃高位
*/
private static int workerCountOf(int c) { return c & CAPACITY; }
/**
* 传入的rs表示线程池运行状态runState,其是高3位有值,低29位全部为0的int,
* 而wc则代表线程池中有效线程的数量workerCount,其为高3位全部为0,而低29位有值得int,
* 将runState和workerCount做或操作|处理,即用runState的高3位,workerCount的低29位填充的数字,而默认传入的
*/
private static int ctlOf(int rs, int wc) { return rs | wc; }
3.4.3 线程池的状态切换
-
RUNNING -> SHUTDOWN // 调用了shutdown()方法
-
(RUNNING 或 SHUTDOWN) -> STOP // 调用了shutdownNow()
-
SHUTDOWN -> TIDYING // 当队列和线程池为空
-
STOP -> TIDYING // 当线程池为空
-
TIDYING -> TERMINATED // 当terminated()钩子方法执行完成
3.4.4 核心方法
- execute():这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行
public void execute(Runnable command) {
// 判断当前任务是否为null,如果为null,直接抛出异常
if (command == null)
throw new NullPointerException();
/*
* Proceed in 3 steps:
* 有以下3个步骤
*
* 1.如果少于corePoolSize的线程在运行,那么试着启动一个新线程,其中用给定指令作为first task。
* 这会调用addWorker去原子性得检查runState和workerCoune,因此可以防止错误报警,在错误报警不应该时通过返回false来添加线程
* 2.如果任务被成功排队,我们任然应该第二次检查是否添加一个新线程(因为可能存在在最后一次检查后挂掉的情况)
* 或者在进入这个方法期间线程池shutdown。所以我们再次检查状态,如果已关闭和有必要则退出队列,或者如果没有的话就开始一个新的线程。
* 3.如果我们无法将task入队,那么我们试图添加新线程。如果失败,那么知道我们shutdown或者是饱和的并拒绝task。
*/
// 获取ctl的初始值。其初始值是:rs | wc,即状态位和线程数量高低位互补
int c = ctl.get();
// 获取当前线程的数量,初始化的时候数量为0,和当前 corePoolSize 比较
if (workerCountOf(c) < corePoolSize) {
// 如果条件成立,调用addWorker(command, true)
// 从addWorker源码分析有得出,只要当前的workerCountOf(c) < corePoolSize 条件成立,就会往线程池里面加入一个线程
// 当前加入的线程会被初始化到Worker中,通过firstTask进行设置
if (addWorker(command, true))
return;
c = ctl.get();
}
// 当前线程池的数量达到corePoolSize的时候
// 验证当前线程池是否处于运行状态。如果处于运行状态。将当前的任务添加到任务队列中。
// offer方法添加一个元素并返回为true。如果队列已满,这返回false
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 添加任务到队列成功以后,在此判断当前线程池是否运行状态
// 如果线程池没有处于运行状态,则从队列中移除当前任务,同时执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 由于存在核心线程的过期策略,可能这个时候当前线程池中的线程已经都过期清理了
// 所以这里进一步的进行检测,获取当前线程的个数。如果线程个数为0的话,则新建一个线程worker
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 如果当前线程池运行正常,且添加任务到队列失败。这时重新启动一个worker线程去执行
// 此时线程池的最多的线程数量,wc < maximumPoolSize
// 尝试直接添一个新的worker线程。如果添加失败,执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
理解
1、当线程数量少于核心线程数量时,新增线程
2、当线程数据大于等于核心线程数量时,将任务加入队列(加入前后检查线程池状态)
3、尝试直接添一个新的worker线程。如果添加失败,执行拒绝策略
-
submit():这个方法也是用来向线程池提交任务的,但是它和execute()方法不同,它能够返回任务执行的结果,去看submit()方法的实现,会发现它实际上还是调用的execute()方法,只不过它利用了Future来获取任务执行结果
-
boolean addWorker(Runnable firstTask, boolean core)
firstTask:任务
core:核心线程还是非核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 获取当前ctl的最新值
int c = ctl.get();
// 获取当前线程池的运行状态
int rs = runStateOf(c);
// Check if queue empty only if necessary.
// 获取当前线程池的状态,如果是STOP,TIDYING,TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false。
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 获取当前线程的数量
int wc = workerCountOf(c);
// 判断当前线程的数量是否超过最大值
// 这里从core的细节上面已经说明。如果为true 则使用 corePoolSize,反之使用 maximumPoolSize
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// 通过CAS设置当前的workerCount:ctl.compareAndSet(expect, expect + 1);
// 当前的线程数量 + 1
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 {
// 初始化Worker对象,传入第一个需要执行的任务
w = new Worker(firstTask);
// 获取 worker对象内部封装的thread线程
final Thread t = w.thread;
if (t != null) {
// 同步代码块,保证当前线程池状态的一致性。因为workers是共享变量
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);//调用decrementWorkerCount将workerCount减一,然后调用tryTerminate停止线程池,并且返回false。
}
return workerStarted;
}
理解
1 、获取当前线程池的状态,如果是STOP,TIDYING,TERMINATED状态的话,则会返回false,如果现在状态是SHUTDOWN,但是firstTask不为空或者workQueue为空的话,那么直接返回false。
2、通过自旋的方式,判断要添加的Worker是否是corePool,如果是的话,那么则判断当前的workerCount是否大于corePoolsize,否则则判断是否大于maximumPoolSize,如果满足的话,说明workerCount超出了线程池大小,直接返回false。如果小于的话,那么判断是否成功将WorkerCount通过CAS操作增加1,如果增加成功的话。则进行到第3步,否则则判断当前线程池的状态,如果现在获取到的状态与进入自旋的状态不一致的话,那么则通过continue retry重新进行状态的判断。
3、如果满足了的话,那么则创建一个新的Worker对象,然后获取线程池的重入锁后,判断当前线程池的状态,如果当前线程池状态为STOP,TIDYING,TERMINATED的话,那么调用decrementWorkerCount将workerCount减一,然后调用tryTerminate停止线程池,并且返回false。
4、如果状态满足的话,那么则在workers中将新创建的worker添加,并且重新计算largestPoolSize,然后启动Worker中的线程开始执行任务。
5、重新Check一次当前线程池的状态,如果处于STOP状态的话,那么就调用interrupt方法中断线程执行。
- 内部类Worker
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
// 完成的任务数量
volatile long completedTasks;
// 构造函数
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
// 初始化成员变量 firstTask
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker */
public void run() {
// 执行当前传入的任务
runWorker(this);
}
// Lock methods
//
// The value 0 represents the unlocked state.
// The value 1 represents the locked state.
protected boolean isHeldExclusively() {
return getState() != 0;
}
protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
}
protected boolean tryRelease(int unused) {
setExclusiveOwnerThread(null);
setState(0);
return true;
}
public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }
void interruptIfStarted() {
Thread t;
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
- void runWorker(Worker w)
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
// 当线程池中的线程数量未达到corePoolSize大小的时候,每次都会先创建一个Worker对象,把当前的任务复制给firstTask
// 直到当前的线程池中的线程数量和corePoolSize大小相等,每次新加任务都会存入到任务队列中
Runnable task = w.firstTask;
// 置空当前的firstTask,主要是为了方便GC
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 当前的firstTask != null
// getTask 获取的任务也不为
while (task != null || (task = getTask()) != null) {
// 获取当前锁资源
w.lock();
// 如果池正在停止,请确保线程被中断;
// 如果没有,请确保线程不被中断。 这个
// 需要在第二种情况下重新检查才能处理
// shutdown在清除中断时正在比赛
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,方便GC
task = null;
// 当前的完成任务++
w.completedTasks++;
// 释放当前的锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 当判断条件 task != null || (task = getTask()) != null 不成立的时候,删除当前Woker
processWorkerExit(w, completedAbruptly);
}
}
- private 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.
// 判断当前的任务队列是否为null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
// 获取当前的线程数量
int wc = workerCountOf(c);
// Are workers subject to culling?
// 如果当前的 allowCoreThreadTimeOut 设置为 ture,或者 wc > corePoolSize 的情况 为 ture
// 当前线程队列已满,才会出现 wc > corePoolSize的
// 通过这段设置,用于判断核心线程空闲时,是否需要清理
// 其次当线程数高于核心线程数时,是否需要清理线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize 的情况
// 判断当前的线程数是否大于最大的线程数
// wc > 1 或者 workQueue为空
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
// 减少当前的线程数量,通过CAS
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
// 根据 timed判断,通过哪种方式获取当前的任务
Runnable r = timed ?
// timed 为false,说明当前线程池线程数量超过了核心数量
// workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 表示等待 keepAliveTime 的时间之后
// 没有任务的话,直接返回 null。而这个返回为null,直接影响当前线程是否被回收的前提条件。
// 线程循环条件:while (task != null || (task = getTask()) != null) 如果 返回 task = null。则直接跳出循环
// 通过 processWorkerExit(w, completedAbruptly) 进行线程的回收
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
// 移除并返回队列的头部元素,如果队列为空,则阻塞
workQueue.take();
// 如果 r != null。 则返回
if (r != null)
return r;
// 如果获取失败,设置当前timeOut为超时,接着循环
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
3.4.4 总结
当往线程池中添加任务的时候,每次添加一个任务都回去新增一个线程。直到不满足 wc < corePoolSize
当前线程池的大小已经达到了corePoolSize的时候,每次添加任务会被存放到阻塞任务队列中。等待执行
等等待任务队列也满的时候,且添加失败。此时在来新的任务,就会接着增加线程的个数,直到满足:wc >= maximumPoolSize ,添加线程失败执行拒绝策略。
线程池中,把线程的状态和数量通过int类型进行维护,高三位表示状态,低29位表示线程数量。这样可以保证线程的状态和数量的一致性
4、多线程创建的工厂类Executors
Executors类里提供了创建适用于各种场景线程池的工具方法(静态方法),常见:
- newFixedThreadPool(int nThreads) (无界队列,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。)
创建一个拥有固定线程数量的线程池,具体的线程数量由nThreads参数指定。最开始该线程池中的线程数为0,之后每提交一个任务就会创建一个线程,直到线程数等于指定的nThreads参数,此后线程数量将不再变化。
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- newCachedThreadPool() (同步移交队列,允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM)
创建一个可缓存的线程池。会为每个任务都分配一个线程,但是如果一个线程执行完任务后长时间(60秒)没有新的任务可执行,该线程将被回收。
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
- newSingleThreadExecutor() (无界队列,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。)
创建单线程的线程池。其实只有一个线程,被提交到该线程的任务将在一个线程中串行执行,并且能确保任务可以按照队列中的顺序串行执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
- newScheduledThreadPool(int corePoolSize) (优先级队列, 允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。)
创建固定线程数量的线程池,而且以延迟或定时的方式来执行任务。
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
理解
1、拒绝策略均使用默认的AbortPolicy(丢弃任务并抛出RejectedExecutionException异常)策略
1)FixedThreadPool 和 SingleThreadPool:
允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool 和 ScheduledThreadPool :
允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
5、总结
1、Executor:接口,执行器,将任务的提交和执行解耦
2、ExecutorService:Executor的子类接口,其提供了生命周期管理的方法,以及可跟踪一个或多个异步任务执行状况返回Future的方法
3、AbstractExecutorService:抽象类,ExecutorService执行方法的默认实现,但未实现Executor的execute方法
4、ThreadPoolExecutor:Executor框架最核心的类,继承AbstractExecutorService,实现了Executor的execute方法
* execute的实现:
1、当线程数量少于核心线程数量时,新增核心线程(addWork())处理任务
2、当线程数量大于等于核心线程数,将任务放入队列
3、队列放满后,新增非核心线程(addWork())处理任务
4、当线程数量大于线程总数时,执行拒绝策略
* addWorker(Runnable firstTask, boolean core)的实现:
1、core决定增加核心线程或者非核心线程
2、new Worker(firstTask),包含一个任务firstTask和一个线程,线程加入线程池并调用start()方法使线程就绪,firstTask作为该线程的第一个任务
3、线程就绪后会执行worker的run方法,run调用runWorker(Worker w)
* runWorker(Worker w)的实现:
1、执行firstTask,完成后置为null
2、firstTask==null,循环从getTask()从队列里获取任务并执行
5、ThreadPoolExecutor构造参数
* corePoolSize:核心线程的个数,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务
* maximumPoolSize:线程池最大线程数,限定为2^29-1。
* keepAliveTime:表示线程没有任务执行时最多保持多久时间会回收。默认只对非核心线程起作用,当调用allowCoreThreadTimeOut(true),对核心线程也起作用
* unit:unit是参数keepAliveTime的时间单位,在TimeUnit类中有7种静态属性。
* workQueue: 一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一般情况下,有几种选择
* ArrayBlockingQueue; //有界队列,基于数组的先进先出队列,此队列创建时必须指定大小;
* PriorityBlockingQueue; //一个无界的阻塞队列
* LinkedBlockingQueue; //有界队列,无参可视为无界队列,基于链表的先进先出队列,如果创建时没有指定此队列大小,则默认为Integer.MAX_VALUE;
* SynchronousQueue; //同步移交队列,这个队列比较特殊,它不会保存提交的任务,而是将直接新建一个线程来执行新来的任务
* 名义上是个队列,但底层并不维护链表也没有维护数组,在一个线程调用它的put方法时会立即将塞入的元素转交给调用take的线程,如果没有调用take的线程则put方法会阻塞
* threadFactory:线程工厂,主要用来创建线程。
* handler:表示当拒绝处理任务时的策略,当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略
* ThreadPoolExecutor.AbortPolicy;//丢弃任务并抛出RejectedExecutionException异常。
* ThreadPoolExecutor.DiscardPolicy;//也是丢弃任务,但是不抛出异常。
* ThreadPoolExecutor.DiscardOldestPolicy;//丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
* ThreadPoolExecutor.CallerRunsPolicy;//由调用线程处理该任务
6、多线程创建的工厂类Executors
- newFixedThreadPool(int nThreads) (无界队列,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。)
- newCachedThreadPool() (同步移交队列,允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM)
- newSingleThreadExecutor() (无界队列,允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。)
- newScheduledThreadPool(int corePoolSize) (延迟队列DelayedWorkQueue, 允许创建的线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。)
7、线程池的执行逻辑:先执行Worker里的run方法,循环从队列获取任务,再执行获取到的任务的run方法,定时或延迟线程池则是将任务本身包装成RunnableScheduledFuture任务,任务的run方法里完成定时或延迟执行逻辑