Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务
的程序都可以使用线程池。开发中,合理使用线程池会带来3个好处:
jdk中Executors
工具类提供了一些常用的线程池。
nThreads
的线程池public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
}
1
的线程池 public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newCachedThreadPool() {
// 核心线程数为0,工作队列不存任务,所有线程全都是非核心线程
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
都是调用ThreadPoolExecutor
构造方法来进行线程池的创建。
阿里巴巴Java开发手册
中明确指出,『不允许』使用Executors创建线程池,生产中尽量使用 ThreadPoolExecutor
的构造方法自定义去创建线程池。如下:
public class ThreadPoolTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(
2, // 核心线程数
5, // 最大线程数
200, // 非核心工作线程在阻塞队列位置等待的时间
TimeUnit.SECONDS, // 非核心工作线程在阻塞队列位置等待的单位
new LinkedBlockingQueue<>(), // 阻塞队列,存放任务的地方
new ThreadPoolExecutor.AbortPolicy() // 拒绝策略:这里有四种
);
for (int i = 0; i < 10; i++) {
MyTask task = new MyTask();
executor.execute(task);
}
// 关闭线程
executor.shutdown();
}
}
// 待执行任务
class MyTask implements Runnable {
@Override
public void run() {
System.out.println("我被执行了....");
}
}
当有新任务到线程池时,线程池执行流程:
提交任务,ThreadPoolExecutor
执行execute
方法分下面4种情况:
ThreadPoolExecutor采取上述步骤的总体设计思路,是为了在执行execute()方法时,尽可能地避免获取全局锁(那将会是一个严重的可伸缩瓶颈)。在ThreadPoolExecutor完成预热之后(当前运行的线程数大于等于corePoolSize),几乎所有的execute()方法调用都是执行步骤2,而步骤2不需要获取全局锁。
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, // 1.核心线程数
int maximumPoolSize, // 2.最大线程数
long keepAliveTime, // 3.非核心线程空闲时终止前等待新任务的最长时间
TimeUnit unit, // 4.keepAliveTime参数的时间单位
BlockingQueue<Runnable> workQueue, // 5.任务队列(阻塞队列),在执行任务之前用于保存任务的队列
ThreadFactory threadFactory, // 6.线程工厂,用来创建新的线程
RejectedExecutionHandler handler) // 7.线程池满时,执行的拒绝策略
{
// 参数校验
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;
}
直接抛出异常
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
调用者处理
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
队列中最久未处理的任务弹出,执行当前任务
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// 将队列中最久未处理的任务弹出
e.getQueue().poll();
// 执行当前任务
e.execute(r);
}
}
}
丢弃被拒绝的任务
。 public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
可以自己实现RejectedExecutionHandler
接口,自定义任务拒绝策略,可以将拒绝的任务放至数据库等存储,等后续在执行。
// 该数值代表两个意思:【按位拆分】
// 高3位表示当前线程池的状态
// 低29位表示当前线程池工作线程的个数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// COUNT_BITS = 29
private static final int COUNT_BITS = Integer.SIZE - 3;
// CAPACITY就是当前工作线程能记录的工作线程的最大个数
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 111:代表RUNNING状态,RUNNING可以处理任务,并且处理阻塞队列中的任务。
private static final int RUNNING = -1 << COUNT_BITS;
// 000:代表SHUTDOWN状态,不会接收新任务,正在处理的任务正常进行,阻塞队列的任务也会做完。
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001:代表STOP状态,不会接收新任务,正在处理任务的线程会被中断,阻塞队列的任务一个不管。
private static final int STOP = 1 << COUNT_BITS;
// 010:代表TIDYING状态,这个状态是否SHUTDOWN或者STOP转换过来的,代表当前线程池马上关闭,就是过渡状态。
private static final int TIDYING = 2 << COUNT_BITS;
// 011:代表TERMINATED状态,这个状态是TIDYING状态转换过来的,转换过来只需要执行一个terminated方法。
private static final int TERMINATED = 3 << COUNT_BITS;
// 基于&运算的特点,保证只会拿到ctl高三位的值 【线程池状态】
private static int runStateOf(int c) { return c & ~CAPACITY; }
// 基于&运算的特点,保证只会拿到ctl低29位的值 【线程池中工作线程状态】
private static int workerCountOf(int c) { return c & CAPACITY; }
线程池的状态变化流程图:
public void execute(Runnable command) {
// 如果当前传过来的任务是null,直接抛出异常即可
if (command == null)
throw new NullPointerException();
// 获取当前的数据值
int c = ctl.get();
//==========================线程池第一阶段:启动核心线程数开始=================================
// Step1:获取ctl低29位的数值(线程池当前工作线程数量),与初始化核心线程数相比
if (workerCountOf(c) < corePoolSize) {
// Step2:添加一个核心线程
if (addWorker(command, true)){
return;
}
// 更新一下当前值
c = ctl.get();
}
//==========================线程池第一阶段:启动核心线程数结束=================================
// 如果走到下面会出现两种情况:
// 1、核心线程数满了,需要往阻塞队列里面扔任务
// 2、核心线程数满了,阻塞队列也满了,执行拒绝策略
//==========================线程池第二阶段:任务放至阻塞队列开始================================
// 判断线程池状态是不是Running的状态(RUNNING可以处理任务,并且处理阻塞队列中的任务)
// 如果是Running的状态,则可以将任务放至阻塞队列中
// 这里如果放阻塞队列失败了,证明阻塞队列满了
if (isRunning(c) && workQueue.offer(command)) {
// 再次更新数值
int recheck = ctl.get();
// 再次校验当前的线程池状态是不是Running
// 如果线程池状态不是Running的话,需要移除刚刚放的任务
if (!isRunning(recheck) && remove(command)){
// 执行拒绝策略
reject(command);
}
// 如果到这里,说明上面阻塞队列中已经有数据了
// 如果线程池中线程个数为0的话,需要创建一个非核心工作线程去执行该任务
// 不能让人家堵塞着
else if (workerCountOf(recheck) == 0){
addWorker(null, false);
}
}
//==========================线程池第二阶段:任务放至阻塞队列结束===========================
// 如果走到这里的逻辑,证明上面的逻辑没走通,有以下两种情况:
// 1、线程池的状态不是Running
// 1.1 如果是这种情况,下面的添加非核心工作线程失败执行拒绝策略,但这个并不是这个逻辑的重点
// 2、阻塞队列添加任务失败(阻塞队列满了)
// 2.1 这种情况才是我们需要关心的
// 2.2 阻塞队列满了,添加非核心工作线程
// 2.3 若添加非核心工作线程失败,证明已经到达maximumPoolSize的限制,执行拒绝策略
//==========================线程池第三阶段:启动非核心线程数开始==============================
// 添加一个非核心工作线程,线程池满了执行下面拒绝策略
else if (!addWorker(command, false))
// 工作队列中添加任务失败,执行拒绝策略 ====线程池第四阶段====
reject(command);
//==========================线程池第三阶段:启动非核心线程数结束=============================
}
submit依旧是执行execute方法逻辑,只不过会将当前任务返回
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
private boolean addWorker(Runnable firstTask, boolean core) {
// 与下面break retry,结束整个循环,continue retry继续执行外层循环
retry:
for (;;) {
// 获取当前线程池的数值(ctl)
int c = ctl.get();
// 获取数值高三位,线程状态
int rs = runStateOf(c);
//==========================线程池状态判断=================================
// rs >= SHUTDOWN:代表当前线程池状态为:SHUTDOWN、STOP、TIDYING、TERMINATED,线程池状态异常
// SHUTDOWN状态(不会接收新任务,正在处理的任务正常进行,阻塞队列的任务也会做完)
// 如果当前的状态是SHUTDOWN状态并且阻塞队列任务不为空且新任务为空
// 需要新起一个非核心工作线程去执行任务
// 如果不是前面的,直接返回false即可
if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null &&
! workQueue.isEmpty()))
return false;
//==========================工作线程个数判断===============================
for (;;) {
// 获取当前线程池中线程的个数
int wc = workerCountOf(c);
// core代表是否是核心线程
// 1、如果线程池线程的个数是否超过了工作线程的最大个数
// 2、根据当前core判断创建的是大于核心线程数(corePoolSize)还是非核心线程数(maximumPoolSize)
if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// cas将线程池中工作线程+1
if (compareAndIncrementWorkerCount(c))
// CAS成功后,直接退出外层循环,代表可以执行添加工作线程操作了。
break retry;
// 获取当前线程池的数值(ctl)
c = ctl.get();
// 再一次获取当前线程池的状态
// 判断当前线程池的状态等不等于上面的rs
// 如果程池的状态被人更改了,需要重新执行外层for循环判断逻辑
if (runStateOf(c) != rs)
continue retry;
}
}
// ...
addWorker()源码后半部分
{boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// 将当前的任务封装成Worker
w = new Worker(firstTask);
// 拿到当前Worker的线程
final Thread t = w.thread;
if (t != null) {
// 【加锁】,保证线程安全(workers、largestPoolSize)
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 获取线程池的状态
int rs = runStateOf(ctl.get());
// 1、rs < SHUTDOWN:保证当前线程池的状态一定是RUNNING状态
// 2、rs == SHUTDOWN && firstTask == null:如果当前线程池是SHUTDOWN状态且新任务为空
if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) {
// 判断当前线程是否存活
if (t.isAlive())
throw new IllegalThreadStateException();
// private final HashSet workers = new HashSet();
// 添加到works中
workers.add(w);
int s = workers.size();
// largestPoolSize线程池中最大线程个数的记录
// 如果当前的大小比最大的还要大,替换即可
if (s > largestPoolSize)
largestPoolSize = s;
// worker添加成功
workerAdded = true;
}
} finally {
// 解锁
mainLock.unlock();
}
// 线程添加成功,随即启动线程
if (workerAdded) {
t.start();
// 线程启动成功
workerStarted = true;
}
}
} finally {
// 线程未启动成功,移除workers中当前worker
if (!workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
// Worker的初始化
Worker(Runnable firstTask) {
setState(-1);
this.firstTask = firstTask;
// 线程工厂创建线程
this.thread = getThreadFactory().newThread(this);
}
// 线程启动失败,移除当前worker
private void addWorkerFailed(Worker w) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
if (w != null)
workers.remove(w);
decrementWorkerCount();
tryTerminate();
} finally {
mainLock.unlock();
}
}
// Worker继承了AQS,目的:工作线程加解锁
// Worker实现了Runnable,执行run方法中自定义内部方法
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{
private static final long serialVersionUID = 6138294804551838833L;
// 当前线程工厂创建的线程(也是执行任务使用的线程)
final Thread thread;
// 当前的第一个任务
Runnable firstTask;
// 记录执行了多少个任务
volatile long completedTasks;
// 构造方法
Worker(Runnable firstTask) {
// 将State设置为-1,代表当前不允许中断线程
setState(-1);
// 设置任务
this.firstTask = firstTask;
// 设置线程
this.thread = getThreadFactory().newThread(this);
}
// 线程启动执行的方法
public void run() {
runWorker(this);
}
// =======================Worker管理中断================================
// 当前方法是中断工作线程时,执行的方法
void interruptIfStarted() {
Thread t;
// 只有Worker中的state >= 0的时候,可以中断工作线程
if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) {
try {
// 如果状态正常,并且线程未中断,中断线程
t.interrupt();
} catch (SecurityException ignore) {
}
}
}
}
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
// 拿到当前的线程
Thread wt = Thread.currentThread();
// 拿到当前Worker的第一个任务
Runnable task = w.firstTask;
// 置空
w.firstTask = null;
// 解锁
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// 如果任务不等于空 或者 从阻塞队列中拿到的任务不等于空 如果队列中拿不到任务一直循环取任务
while (task != null || (task = getTask()) != null) {
// 加锁
w.lock();
// 如果线程池状态 >= STOP,确保线程中断
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;
// 任务执行数+1
w.completedTasks++;
// 解锁
w.unlock();
}
}
completedAbruptly = false;
} finally {
// 移除worker,累加线程完成任务数
processWorkerExit(w, completedAbruptly);
}
}
private Runnable getTask() {
// 超时的标记
boolean timedOut = false;
// 死循环拿数据
for (;;) {
// 拿到当前的ctl
int c = ctl.get();
// 获取其线程池状态
int rs = runStateOf(c);
// 如果线程池状态是STOP,没有必要处理阻塞队列任务,直接返回null
// 如果线程池状态是SHUTDOWN,并且阻塞队列是空的,直接返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
// 线程池中的线程个数-1
decrementWorkerCount();
return null;
}
// 当前线程池中线程个数
int wc = workerCountOf(c);
// 这里是个重点
// allowCoreThreadTimeOut:是否允许核心线程数超时(开启之后),核心线程数也会执行下面的超时逻辑
// wc > corePoolSize:当前线程池中的线程个数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
// wc > maximumPoolSize:基本不存在
// timed && timedOut:第一次肯定是失败的(timedOut = false)
if ((wc > maximumPoolSize || (timed && timedOut))
// 1、线程个数 > 1
// 2、阻塞队列是空的
&& (wc > 1 || workQueue.isEmpty())) {
// 线程池的线程个数-1
if (compareAndDecrementWorkerCount(c)){
return null;
}
continue;
}
try {
// 根据前面的timed的值(当前线程池中的线程个数是否大于核心线程数)
// 如果大于,执行workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)带有时间的等待,超过时间无任务,会返回null
// 如果小于等于,执行workQueue.take(),死等任务,不会返回null 【获取队列中任务】
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take();
// 任务不为空,返回队列中任务
if (r != null){
return r;
}
// 到这里,说明获取任务超时 timedOut置为true
// 上面 if((wc > maximumPoolSize || (timed && timedOut)) (timed && timedOut) 为 true
// wc > 1 || workQueue.isEmpty():当线程大于1或者阻塞队列无数据,直接返回null,减少线程个数
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
private void processWorkerExit(Worker w, boolean completedAbruptly) {
// 正常退出: completedAbruptly=false
// 出现异常退出: completedAbruptly=true
if (completedAbruptly)
decrementWorkerCount();
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 将当前worker的执行任务数累加到线程池中
completedTaskCount += w.completedTasks;
// 线程池移除该工作线程
workers.remove(w);
} finally {
// 解锁
mainLock.unlock();
}
tryTerminate();
// 获取ctl的数据
int c = ctl.get();
// 只有SHUTDOWN、RUNNING会进入判断
if (runStateLessThan(c, STOP)) {
// 任务执行结束,正常退出
if (!completedAbruptly) {
// 是否允许核心线程超时 允许:0;不允许:核心线程数
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
// 如果min=0并且阻塞队列不为空,将min设置成1
if (min == 0 && ! workQueue.isEmpty())
min = 1;
// 当前线程数量大于等于最小值,直接返回即可
if (workerCountOf(c) >= min){
return;
}
}
// 执行到这,说明线程池中没有线程了,并且还有阻塞任务
// 只能添加一个非核心线程去处理这些任务
addWorker(null, false);
}
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 将线程池状态改为stop
advanceRunState(STOP);
// 将所有工作线程中断
interruptWorkers();
// 将工作队列中的任务全部移除(任务队列中未执行的任务)
tasks = drainQueue();
} finally {
// 解锁
mainLock.unlock();
}
// 查看当前线程池是否可以变为TERMINATED状态
// 从 Stop 状态修改为 TIDYING,再修改为 TERMINATED
tryTerminate();
return tasks;
}
// 将线程池的状态修改为Stop
private void advanceRunState(int targetState) {
// 进来直接死循环
for (;;) {
// 获取当前的ctl
int c = ctl.get();
// runStateAtLeast(c, targetState):当前的c是不是大于STOP(如果大于Stop的话,说明线程池状态已经终止
// 基于CAS,将ctl从c修改为Stop状态,不修改工作线程个数,仅仅将状态修改为Stop
// 如果可以修改成功,直接退出即可
if (runStateAtLeast(c, targetState) || ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
// 将线程池中的线程全部中断
private void interruptWorkers() {
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 循环遍历线程组
for (Worker w : workers)
// 中断线程
w.interruptIfStarted();
} finally {
// 解锁
mainLock.unlock();
}
}
// 删除工作队列中所有任务
private List<Runnable> drainQueue() {
// 任务队列
BlockingQueue<Runnable> q = workQueue;
// 返回的结果
ArrayList<Runnable> taskList = new ArrayList<Runnable>();
q.drainTo(taskList);
if (!q.isEmpty()) {
// 将任务从workQueue移除并且放到taskList中
for (Runnable r : q.toArray(new Runnable[0])) {
if (q.remove(r))
taskList.add(r);
}
}
// 最终返回即可
return taskList;
}
final void tryTerminate() {
for (;;) {
// 拿到ctl
int c = ctl.get();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// CAS将当前的ctl设置成TIDYING
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
// 【钩子函数】,在线程池销毁之前可以做一些自定义操作
terminated();
} finally {
// 将ctl设置成TERMINATED标志着线程池的正式结束
ctl.set(ctlOf(TERMINATED, 0));
// 线程池提供了一个方法,主线程在提交任务到线程池后,是可以继续做其他操作的。
// 线程池终止后,要唤醒那些调用了awaitTermination方法的线程(等线程池一段时间,此时主线程可以做自己的事)
// 【当时等待线程池返回的主线程,虽然线程池已经销毁了,但他们也必须要唤醒,不能一直死等】
termination.signalAll();
}
return;
}
} finally {
// 解锁
mainLock.unlock();
}
}
}
public void shutdown() {
// 加锁
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
// 将线程池状态修改为SHUTDOWN
advanceRunState(SHUTDOWN);
// 将线程池中的线程全部中断
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
// 查看当前线程池是否可以变为TERMINATED状态
// 从 SHUTDOWN 状态修改为 TIDYING,再修改为 TERMINATED
tryTerminate();
}
想要合理地配置线程池,就必须首先分析任务特性
,有几个角度分析:
针对不同性质任务,采用不同线程池配置:
CPU密集型任务应配置尽可能小的线程,如配置Ncpu+1个线程的线程池。
IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如2*Ncpu。
混合型的任务
,如果可以拆分,将其拆分成一个CPU密集型任务和一个IO密集型任务
,只要这两个任务执行的时间相差不是太大,那么分解后执行的吞吐量将高于串行执行的吞吐量。如果这两个任务执行时间相差太大,则没必要进行分解。可以通过Runtime.getRuntime().availableProcessors()
方法获得当前设备的CPU个数。优先级不同的任务可以使用优先级队列PriorityBlockingQueue来处理
。它可以让优先级高的任务先执行。建议使用有界队列。
有界队列能增加系统的稳定性和预警能力,可以根据需要设大一点儿,比如几千。
如果在系统中大量使用线程池,则有必要对线程池进行监控,方便在出现问题时,可以根据线程池的使用状况快速定位问题。
通过线程池提供的参数进行监控,在监控线程池的时候可以使用以下属性:
taskCount
:线程池需要执行的任务数量。completedTaskCount
:线程池在运行过程中已完成的任务数量,小于或等于taskCount。largestPoolSize
:线程池里曾经创建过的最大线程数量。通过这个数据可以知道线程池是否曾经满过。如该数值等于线程池的最大大小,则表示线程池曾经满过。getPoolSize
:线程池的线程数量。如果线程池不销毁的话,线程池里的线程不会自动销毁,所以这个大小只增不减。getActiveCount
:获取活动的线程数。通过扩展线程池进行监控。可以通过继承线程池来自定义线程池,重写线程池的beforeExecute、afterExecute和terminated
方法,在任务执行前、执行后和线程池关闭前
执行一些代码来进行监控。
可以监控任务的平均执行时间、最大执行时间和最小执行时间等。这几个方法在线程池里是空方法。
参考资料: