ThreadPoolExecutor 线程池原理分析
1.介绍
1.1 整体结构
1.2 相关接口,类,方法
Executor 顶层接口
一个运行新任务的简单接口,只声明了一个方法:
void execute(Runnable command);
返回值为 void,参数为Runnable 类型,用来执行传进去的任务的;
ExecutorService 接口
继承了 Executor 接口,声明一些方法:submit、invokeAll、invokeAny 以及shutDown 等;
AbstractExecutorService抽象类
实现了 ExecutorService 接口,基本实现了 ExecutorService 中声明的所有方法;
ThreadPoolExecutor 类
继承了类 AbstractExecutorService。
1.3 ThreadPoolExecutor核心属性和方法
execute() 方法: Executor 中声明的方法,ThreadPoolExecutor 具体的实现,是核心方法,通过这个方法可以向线程池提交一个任务,交由线程池去执行。
submit( )方法: ExecutorService 中声明的方法,在 AbstractExecutorService 具体的实现,在ThreadPoolExecutor 中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和 execute() 方法不同,它能够返回任务执行的结果, submit() 实际上还是调用的 execute() 方法,只不过它利用了Future 来获取任务执行结果。
shutdown()和 shutdownNow():是用来关闭线程池的。
内部类Worker: 线程池的工作线程类
workers属性:一个Set集合,储存线程池的所有工作线程Worker;
addWorker方法:加入到当前线程池,开启线程并执行;
runWorker:执行task;
processWorkerExit:清理线程池中的线程
还有很多其他的方法:getQueue() 、getPoolSize() 、getActiveCount()、getCompletedTaskCount() 等获取与线程池相关属性的方法,
2. ThreadPoolExecutor分析
2.1 线程池状态
线程池一共有五种状态, 各状态值所代表的的含义和该状态值下可执行的操作,如下:
RUNNING |
接收新任务,并且也能处理阻塞队列中的任务。 |
SHUTDOWN |
不接收新任务,但是却可以继续处理阻塞队列中的任务。 |
STOP |
不接收新任务,同时也不处理队列任务,并且中断正在进行的任务。 |
TIDYING |
所有任务都已终止,workercount(有效线程数)为0,线程转向 TIDYING 状态将会运行 terminated() 钩子方法。 |
TERMINATED |
terminated() 方法调用完成后变成此状态。 |
生命周期状态流转如下图所示:
进入TERMINATED的条件如下:
-
线程池不是RUNNING状态;
-
线程池状态不是TIDYING状态或TERMINATED状态;
-
如果线程池状态是SHUTDOWN并且workerQueue为空;
-
workerCount为0;
-
设置TIDYING状态成功。
2.2 ctl
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //31-3=29位
private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;
// runState 状态通过int表示.,所有状态左移29位,
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;
// Packing and unpacking ctl
private static int runStateOf(int c) { return c & ~COUNT_MASK; } //获取运行状态;
private static int workerCountOf(int c) { return c & COUNT_MASK; } //获取活动线程数
private static int ctlOf(int rs, int wc) { returnrs | wc; } //获取运行状态和活动线程数的值。
线程池内部使用一个变量维护两个值:运行状态(runState)和线程数量 (workerCount) 将十进制 int 值转换为二进制的值,共32位,高3位代表运行状态(runState ),低29位代表工作线程数(workerCount)。ctlOf方法,将这两个值进行位或运算。
2.3 构造方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) ;
其他几个方法都是调用到这个构造方法 ;
- corePoolSize:
为线程池的核心线程基本大小。
当线程池中的线程数目达到 corePoolSize 后,就会把到达的任务放到缓存队列当中;
- maximumPoolSize
为线程池最大线程大小。
- keepAliveTime 和 unit
则是线程空闲后的存活时间。线程池维护线程所允许的空闲时间。当线程池中的线程数量 > corePoolSize的时候,如果这时没有新的任务提交,核心线程外的线程不会立即销毁,而是会等待,直到等待的时间超过了keepAliveTime;
- workQueue:
用于存放任务的阻塞队列。等待队列,当任务提交时,如果线程池中的线程数量大于等于corePoolSize的时候,把该任务封装成一个Worker对象放入等待队列;
一般来说,这里的阻塞队列有以下几种选择:
直接切换:这种方式常用的队列是SynchronousQueue;
使用无界队列:一般使用基于链表的阻塞队列LinkedBlockingQueue。如果使用这种方式,那么线程池中能够创建的最大线程数就是corePoolSize,而maximumPoolSize就不会起作用了。当线程池中所有的核心线程都是RUNNING状态时,这时一个新的任务提交就会放入等待队列中。
使用有界队列:一般使用ArrayBlockingQueue。使用该方式可以将线程池的最大线程数量限制为maximumPoolSize,这样能够降低资源的消耗,但同时这种方式也使得线程池对线程的调度变得更困难,因为线程池和队列的容量都是有限的值,所以要想使线程池处理任务的吞吐率达到一个相对合理的范围,又想使线程调度相对简单,并且还要尽可能的降低线程池对资源的消耗,就需要合理的设置这两个数量。
- threadFactory:
用来创建新线程。默认使用Executors.defaultThreadFactory() 来创建线程。使用默认的ThreadFactory来创建线程时,会使新创建的线程具有相同的NORM_PRIORITY优先级并且是非守护线程,同时也设置了线程的名称。
- handler:(拒绝策略)
当队列和最大线程池都满了之后的饱和策略。RejectedExecutionHandler类型的变量,表示线程池的饱和策略。如果阻塞队列满了并且没有空闲的线程,这时如果继续提交任务,就需要采取一种策略处理该任务。线程池提供了4种策略:
AbortPolicy:直接抛出异常,这是默认策略;
CallerRunsPolicy:用调用者所在的线程来执行任务;
DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务;
DiscardPolicy:直接丢弃任务;
2.4 Worker类
2.4.1 简单说明
Worker 是 ThreadPoolExecutor的一个内部类,主要是用来维护线程执行任务的中断控制状态,它实现了Runnable 接口同时继承了AQS,实现 Runnable 接口意味着 Worker 就是一个线程,继承 AQS 是为了实现独占锁这个功能。线程池中的每一个线程被封装成一个Worker对象,ThreadPool维护的其实就是一组Worker对象;
2.4.2 Worker类定义
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
{
private static final long serialVersionUID = 6138294804551838833L;
final Thread thread; // 处理任务的线程。
Runnable firstTask; //用来保存传入的任务
/** Per-thread task counter */
volatile long completedTasks;
//构造函数,初始化AQS的state值为-1
Worker(Runnable firstTask) {
setState(-1); // AQS中默认的state是0,如果刚创建了一个Worker对象,还没有执行任务时,这时就不应该被中断, 将state设置为-1是为了禁止在执行任务前对线程进行中断。
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);
}
/** Delegates main run loop to outer runWorker. */
public void run() {
runWorker(this);
}
//state 值0表示未锁定状态。1表示锁定
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) {
}
}
}
}
2.4.3 runWorker方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask; // 获取第一个任务
w.firstTask = null;
w.unlock(); // 允许中断
boolean completedAbruptly = true; // 是否因为异常退出循环
try { // 如果task为空,则通过getTask来获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
// 如果线程池正在停止,那么要保证当前线程是中断状态;
如果不是的话,则要保证当前线程不是中断状态; 这里要考虑在执行该if语句期间可能也执行了shutdownNow方法,shutdownNow方法会把状态设置为STOP,
// 使用Thread.interrupted()来判断是否中断 ,是为了确保在RUNNING或者SHUTDOWN状态时线程是非中断状态的,因为Thread.interrupted()方法会复位中断的状态。
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task); //ThreadPoolExecutor类中空方法,留给子类来实现。
try {
task.run();
afterExecute(task, null); //ThreadPoolExecutor类中空方法,留给子类来实现
} catch (Throwable ex) {
afterExecute(task, ex);
throw ex;
}
} finally {
task = null;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
总结一下执行过程:
1. while循环不断地通过getTask()方法获取任务;
2. getTask()方法从阻塞队列中取任务;
3. 如果线程池正在停止,那么要保证当前线程是中断状态,否则要保证当前线程不是中断状态;
4. 调用 task.run()执行任务;
5. 如果task为null则跳出循环,执行processWorkerExit()方法;
6. runWorker方法执行完毕,也代表着Worker中的run方法执行完毕,销毁线程。
2.4.4 getTask方法
private Runnable getTask() {
boolean timedOut = false; // 表示上次从阻塞队列中取任务时是否超时( poll() 方法)
for (;;) {
int c = ctl.get();
// 如果有必要检查队列为空
/ * 如果线程池状态rs >= SHUTDOWN,也就是非RUNNING状态,再进行以下判断:
* 1. rs >= STOP,线程池是否正在stop;
* 2. 阻塞队列是否为空。
* 如果以上条件满足,则将workerCount减1并返回null。
* 如果当前线程池状态的值是SHUTDOWN或以上时,不允许再向阻塞队列中添加任务。
*/
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP) || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// timed变量用于判断是否需要进行超时控制。
// allowCoreThreadTimeOut默认是false,也就是核心线程不允许进行超时;
// wc > corePoolSize,表示当前线程池中的线程数量大于核心线程数量;
// 对于超过核心线程数量的这些线程,需要进行超时控制
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
/* wc > maximumPoolSize,可能是因为同时执行了setMaximumPoolSize方法;
timed && timedOut =true,表示当前操作 超时控制+上次从阻塞队列中获取任务发生了超时;
如果有效线程数量大于1,或者阻塞队列是空的,将workerCount减1;
如果减1失败,则返回重试。
如果wc == 1时,也就说明当前线程是线程池中唯一的一个线程了。 */
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue; // 如果减1失败,则返回重试
}
//如果没有上面的情况则conf队列中取任务
try {
/// 如果timed为true,通过poll()方法做超时拉取,keepAliveTime时间内没有等待到有效的任务,则返回null
// 如果timed为false,通过take()做阻塞拉取,会阻塞到有下一个有效的任务时候再返回(一般不会是null)
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //获取并删除 该队列的头,等待一定时间
workQueue.take(); //阻塞直到队列不为空
if (r != null)
return r;
timedOut = true; // 如果 r == null,说明已经超时,timedOut设置为true
} catch (InterruptedException retry) {
// 如果获取任务时当前线程发生了中断,则设置timedOut为false并返回循环重试
timedOut = false;
}
}
}
这里重要的地方是第二个if判断,目的是控制线程池的有效线程数量。由上文中的分析可以知道,在执行execute方法时,如果当前线程池的线程数量超过了corePoolSize且小于maximumPoolSize,并且workQueue已满时,则可以增加工作线程,但这时如果超时没有获取到任务,也就是timedOut为true的情况,说明workQueue已经为空了,也就说明了当前线程池中不需要那么多线程来执行任务了,可以把多于corePoolSize数量的线程销毁掉,保持线程数量在corePoolSize即可。
什么时候会销毁?
当然是runWorker方法执行完之后,也就是Worker中的run方法执行完,由JVM自动回收。
getTask方法返回null时,在runWorker方法中会跳出while循环,然后会执行processWorkerExit方法。
2.4.5 processWorkerExit方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 如果completedAbruptly值为true,说明线程执行时出现了异常,需要将workerCount减1;
// 如果线程执行时没有出现异常,说明在getTask()方法中已经已经对workerCount进行了减1操作,这里就不必再减了。
if (completedAbruptly)
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
//统计完成的任务数
completedTaskCount += w.completedTasks;
workers.remove(w); // 从workers中移除,表示着从线程池中移除了一个工作线程
} finally {
mainLock.unlock();
}
tryTerminate(); // 根据线程池状态进行判断是否结束线程池
int c = ctl.get();
//当线程池是RUNNING或SHUTDOWN状态时,
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
//allowCoreThreadTimeOut=true,且等待队列有任务,至少保留一个worker
min = 1;
if (workerCountOf(c) >= min) // workerCount>=corePoolSize则返回
return;
}
addWorker(null, false); //如果worker是异常结束,那么会直接addWorker
}
}
至此,processWorkerExit执行完之后,工作线程被销毁,以上就是整个工作线程的生命周期,从execute方法开始,Worker使用ThreadFactory创建新的工作线程,runWorker通过getTask获取任务,然后执行任务,如果getTask返回null,进入processWorkerExit方法,整个线程结束,如图所示:
2.4.6 tryTerminate方法
final void tryTerminate() {
for (;;) {
int c = ctl.get();
/*
* 当前线程池的状态为以下几种情况时,直接返回:
* 1. RUNNING,因为还在运行中,不能停止;
* 2. TIDYING或TERMINATED,因为线程池中已经没有正在运行的线程了;
* 3. SHUTDOWN并且等待队列非空,这时要执行完workQueue中的task;
*/
if (isRunning(c) ||
runStateAtLeast(c, TIDYING) ||
(runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
return;
// 如果线程数量不为0,则中断一个空闲的工作线程,并返回
if (workerCountOf(c) != 0) { // Eligible to terminate
interruptIdleWorkers(ONLY_ONE); //中断空闲线程
return;
}
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 这里尝试设置状态为TIDYING,如果设置成功,则调用terminated方法
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated(); // 默认什么都不做,留给子类实现
} finally {
ctl.set(ctlOf(TERMINATED, 0)); // 设置状态为TERMINATED
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
interruptIdleWorkers(ONLY_ONE); 的作用是因为在getTask方法中执行 workQueue.take()时,如果不执行中断会一直阻塞。
在下面介绍的shutdown方法中,会中断所有空闲的工作线程,如果在执行shutdown时工作线程没有空闲,然后又去调用了getTask方法,这时如果workQueue中没有任务了,调用workQueue.take() 时就会一直阻塞。所以每次在工作线程结束时调用tryTerminate方法来尝试中断一个空闲工作线程,避免在队列为空时取任务一直阻塞的情况。
2.4.7 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(); // 尝试结束线程
}
这里思考一个问题:在runWorker方法中,执行任务时对Worker对象w进行了lock操作,为什么要在执行任务的时候对每个工作线程都加锁呢?
下面仔细分析一下:
- 在getTask方法中,如果这时线程池的状态是SHUTDOWN并且workQueue为空,那么就应该返回null来结束这个工作线程,而使线程池进入SHUTDOWN状态需要调用shutdown方法;
- shutdown方法会调用interruptIdleWorkers来中断空闲的线程,interruptIdleWorkers持有mainLock,会遍历workers来逐个判断工作线程是否空闲。但getTask方法中没有mainLock;
在getTask中,如果判断当前线程池状态是RUNNING,并且阻塞队列为空,那么会调用
workQueue.take() 进行阻塞;
- 如果在判断当前线程池状态是RUNNING后,这时调用了shutdown方法把状态改为了SHUTDOWN,这时如果不进行中断,那么当前的工作线程在调用了workQueue.take()
后会一直阻塞而不会被销毁,因为在SHUTDOWN状态下不允许再有新的任务添加到workQueue中,这样一来线程池永远都关闭不了了;
- 由上可知,shutdown方法与getTask方法(从队列中获取任务时)存在竞态条件;
解决这一问题就需要用到线程的中断,也就是为什么要用interruptIdleWorkers方法。在调用
workQueue.take() 时,如果发现当前线程在执行之前或者执行期间是中断状态,则会抛出InterruptedException,解除阻塞的状态;
- 但是要中断工作线程,还要判断工作线程是否是空闲的,如果工作线程正在处理任务,就不应该发生中断;
- 所以Worker继承自AQS,在工作线程处理任务时会进行lock,interruptIdleWorkers在进行中断时会使用tryLock来判断该工作线程是否正在处理任务,如果tryLock返回true,说明该工作线程当前未执行任务,这时才可以被中断。
2.4.8 interruptIdleWorkers方法
private void interruptIdleWorkers() {
interruptIdleWorkers(false);
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try { // 遍历workers中所有的工作线程
for (Worker w : workers) {
Thread t = w.thread;
// 若线程没有被中断且 tryLock成功,就中断该线程
if (!t.isInterrupted() && w.tryLock()) {
try {
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
为什么需要持有mainLock?因为workers是HashSet类型的,不能保证线程安全。
2.4.9 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(); // 尝试结束线程使线程池的状态设置为TERMINATED
return tasks;
}
shutdownNow方法与shutdown方法类似,不同的地方在于:
- 设置状态为STOP;
- 中断所有工作线程,无论是否是空闲的;
- 取出阻塞队列中没有被执行的任务并返回。
2.5 execute()方法
2.5.1 execute() 提交任务
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get(); // clt记录着runState和workerCount
if (workerCountOf(c) < corePoolSize) { //活动线程 < 核心线程数, 新建一个线程放入线程池中,并把任务添加到该线程中
// addWorker中的第二个参数表示限制添加线程的数量是根据corePoolSize来判断还是maximumPoolSize来判断; 如果为true,根据corePoolSize来判断;false,则根据maximumPoolSize来判断
if (addWorker(command, true))
return;
c = ctl.get(); // 如果添加失败,则重新获取ctl值
}
// 如果当前线程池是运行状态并且任务添加到队列成功
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get(); // 重新获取ctl值
// 再次判断线程池的运行状态,如果不是 , 这时需要移除之前添加到workQueue中 的 command
// 执行过后通过handler使用拒绝策略对该任务进行处理,整个方法返回
if (! isRunning(recheck) && remove(command))
reject(command);
//获取线程池中的有效线程数,如果是0,则执行addWorker方法
else if (workerCountOf(recheck) == 0)
/ * 第一个参数为null,表示在线程池中创建一个线程,但不去启动;
* 第二个参数为false,将线程池的有限线程数量的上限设置为maximumPoolSize,添加线程时根据maximumPoolSize来判断;
* 如果判断workerCount大于0,则直接返回,在workQueue中新增的command会在将来的某个时刻被执行。*/
addWorker(null, false);
}
/ * 如果执行到这里,有两种情况:
* 1. 线程池已经不是RUNNING状态;
* 2. 线程池是RUNNING状态,但workerCount >= corePoolSize并且workQueue已满。
* 这时,再次调用addWorker方法,但第二个参数传入为false,将线程池的有限线程数量的上限设置为maximumPoolSize;
*/
else if (!addWorker(command, false))
reject(command); //如果失败则拒绝该任务
}
execute执行流程
简单来说,在执行execute()方法时如果状态一直是RUNNING时,的执行过程如下:
1. 如果workerCount < corePoolSize 则创建并启动一个线程来执行新提交的任务,即使线程池中的其他线程是空闲的;
2.如果 workerCount >= corePoolSize 且线程池内的阻塞队列workQueue未满,则将任务添加到该阻塞队列中;
3.如果 workerCount >= corePoolSize && workerCount < maximumPoolSize 且线程池内的阻塞队列已满,则创建并启动一个线程来执行新提交的任务;
4.如果 workerCount >= maximumPoolSize,并且线程池内的阻塞队列已满, 则根据拒绝策略来处理该任务, 默认的处理方式是直接抛异常。
这里要注意一下 addWorker(null, false);,也就是创建一个线程,但并没有传入任务,因为任务已经被添加到workQueue中了,所以worker在执行的时候,会直接从workQueue中获取任务。所以,在workerCountOf(recheck) == 0时执行 addWorker(null, false);
也是为了保证线程池在RUNNING状态下必须要有一个线程来执行任务。
任务提交时,判断的顺序为 corePoolSize –> workQueue –> maximumPoolSize。
execute方法执行流程图如下:
2.5.2 addWorker方法
作用: 在线程池中创建一个新的线程并执行,
参数:
firstTask参数 用于指定新增的线程执行的第一个任务,
core参数为true表示在新增线程时会判断当前活动线程数是否少于corePoolSize,false表示新增线程前需要判断当前活动线程数是否少于maximumPoolSize,
private boolean addWorker(Runnable firstTask, boolean core) {
//外部循环体,这样看来,作用是不断的校验当前的线程池状态是否能接受新任务,如果校验通过了之后才能继续往下运行
retry:
for (int c = ctl.get();;) { // 获取运行状态 c
//如果 状态值 >= SHUTDOWN,则表示此时不再接收新任务,但可以继续处理阻塞队列中已经保存的任务,
// 所以在新任务firstTask不为空的时候会返回false;
// STOP状态也返回false
// 队列为空,表示没有任务,不需要再添加线程,返回false
if (runStateAtLeast(c, SHUTDOWN)
&& (runStateAtLeast(c, STOP)
|| firstTask != null
|| workQueue.isEmpty()))
return false;
for (;;) {
// 如果 工作线程 超过 一定数量,返回false;
// core参数为true表示根据corePoolSize来比较,
// core参数为false则根据maximumPoolSize来比较。
if (workerCountOf(c)
>= ((core ? corePoolSize : maximumPoolSize) & COUNT_MASK))
return false;
if (compareAndIncrementWorkerCount(c))
break retry; // 尝试增加workerCount,如果成功,则跳出第一个for循环
c = ctl.get(); // 如果增加workerCount失败,则重新获取ctl的值
if (runStateAtLeast(c, SHUTDOWN))
continue retry; // 如果线程池状态发生变化,重新从最外层循环
// else CAS failed due to workerCount change; retry inner loop
}
} //外部循环体-end
//-----------第二部分代码-----------
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask); //根据firstTask来创建Worker对象
final Thread t = w.thread; // 每一个Worker对象都会创建一个线程
if (t != null) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
int c = ctl.get();
// 如果线程状态是RUNNING或者是SHUTDOWN状态并且firstTask为null,向线程池中添加线程。因为在SHUTDOWN时不会添加新的任务,但还是会执行workQueue中的任务
if (isRunning(c) ||
(runStateLessThan(c, STOP) && 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;
}
4.其他
4.1 线程池的监控
通过线程池提供的参数进行监控。线程池里有一些属性在监控线程池的时候可以使用
- getTaskCount:线程池已经执行的和未执行的任务总数;
- getCompletedTaskCount:线程池已完成的任务数量,该值小于等于taskCount;
- getLargestPoolSize:线程池曾经创建过的最大线程数量。通过这个数据可以知道线程池是否满过,也就是达到了maximumPoolSize;
- getPoolSize:线程池当前的线程数量;
- getActiveCount:当前线程池中正在执行任务的线程数量。
通过这些方法,可以对线程池进行监控,在ThreadPoolExecutor类中提供了几个空方法,如beforeExecute方法,afterExecute方法和terminated方法,可以扩展这些方法在执行前或执行后增加一些新的操作,例如统计线程池的执行任务的时间等,可以继承自ThreadPoolExecutor来进行扩展。
参考:
http://www.ideabuffer.cn/2017/04/04/%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3Java%E7%BA%BF%E7%A8%8B%E6%B1%A0%EF%BC%9AThreadPoolExecutor/
https://blog.csdn.net/lvxinchun/article/details/108830502
https://mp.weixin.qq.com/s/31OUukd-bbBbs-pJpKE_cQ
https://www.cnblogs.com/jajian/p/11442929.html