线程数、核心线程数量如何设置
我们知道在线程池创建过程中2个重要参数
corePoolSize:核心线程数
maximumPoolSize:最大线程数
通常我们怎么设置呢?
理论上
将任务分为CPU密集型、IO密集型、及混合型。
CPU密集型:CPU核心数+1
为什么要+1,在多线程中,如果当前线程中断,CPU就会让出当前线程调度其他线程,这里+1就是充分利用CPU资源
IO密集型:2*CPU核心数
通用公式: CPU 核心数 * (1 + 线程等待时间 / 线程运行总时间)
实际上
当然现实中一个系统中不可能只有我们当前一个线程池在运行,根据理论值配置,对项目接口进行压测,在保证响应时间的情况下,根据压测结果动态调整线程数量。
线程池的执行方法
- void execute(Runnable command)
- Future<?> submit(Runnable task)
- invokeAll() //执行一组任务,按任务集合保存结果
- invokeAny() //执行一组任务,返回任意一个执行成功的结果
- schedule() //延迟执行或者定期执行
线程池的执行流程
- 通过execute()或submit()方法提交任务到线程池;
-
如果当前工作线程数 < corePoolSize,创建新线程执行任务,即使有空闲线程也创建新线程(直到达到corePoolSize)
-
如果工作线程数 ≥ corePoolSize,尝试将任务放入工作队列
-
如果队列未满,任务加入队列等待执行
-
如果队列已满且工作线程数 < maximumPoolSize,创建临时线程执行任务
-
如果队列已满且工作线程数 = maximumPoolSize,触发拒绝策略
线程的五种状态流转
RUNNING:会接收新任务并且会处理队列中的任务
SHUTDOWN:不会接收新任务并且会处理队列中的任务
STOP:不会接收新任务并且不会处理队列中的任务,并且会中断在处理的任务
TIDYING:所有任务都终止了,线程池中也没有线程了,这样线程池的状态就会转为TIDYING,一旦达到此状态,就会调用线程池的terminated()
TERMINATED:terminated()执行完之后就会转变为TERMINATED
这五种状态并不能任意转换,只会有以下几种转换情况:
- RUNNING -> SHUTDOWN:手动调用shutdown()触发
- (RUNNING or SHUTDOWN) -> STOP:调用shutdownNow()触发,如果先调shutdown()紧着调shutdownNow(),就会发生SHUTDOWN -> STOP
- shutdownNow(),就会发生SHUTDOWN -> STOP
- SHUTDOWN -> TIDYING:队列为空并且线程池中没有线程时自动转换
- STOP -> TIDYING:线程池中没有线程时自动转换(队列中可能还有任务)
- TIDYING -> TERMINATED:terminated()执行完后就会自动转换
线程池中的线程是如何关闭的?
非核心线程数回收
-
条件:当线程空闲时间超过
keepAliveTime
且当前线程数大于corePoolSize
-
过程:线程池会主动终止这些多余的线程
-
效果:线程数量逐渐减少到
corePoolSize
核心线程数回收
要设置
executor.allowCoreThreadTimeOut(true);
使用shutdown() / shutdownNow() 中断,底层 interrupt()来中断线程
源码解析
基本属性
//这个下面用到,高低位,高3位,设置线程池状态,后29位设置线程数量
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; // 32-3 = 29
private static final int CAPACITY = (1 << COUNT_BITS) - 1; // 线程数量掩码
// 线程池状态,存储在高位
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; // 所有任务已终止,线程数为0,转换为TERMINATED前的状态
private static final int TERMINATED = 3 << COUNT_BITS; // 终止状态
// 从ctl中提取线程数
private static int workerCountOf(int c) { return c & CAPACITY; }
// 从ctl中提取运行状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//从ctl里面提取运行状态(runState)和线程数(workerCount)合并为一个整数值
private static int ctlOf(int rs, int wc) { return rs | wc; }
初始化方法
并没有立刻创建所有核心数,在调用execute时创建,属于一种懒加载。
但是在Tomcat类中ThreadPoolExecutor是在初始化时创建所有核心线程数
execute方法
流程:
1.检查任务是否为空,若为空则抛出NullPointerException。
2.如果当前线程数小于核心线程数,尝试创建新线程执行任务。如果成功,则返回;否则重新获取状态。
3.如果线程池处于运行状态且任务能成功入队,则再次检查线程池状态:
如果线程池已停止,移除任务并拒绝执行。
如果线程池中没有线程,尝试创建一个空闲线程。
4.如果任务无法入队,尝试创建新线程执行任务。如果失败,则拒绝任务。
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
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);
//如果线程池中没有线程,尝试创建一个空闲线程,针对核心线程回收情况(allowCoreThreadTimeOut 设置为true)
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/**
如果任务无法入队,尝试创建新线程执行任务。如果失败,则拒绝任务
**/
else if (!addWorker(command, false))
reject(command);
}
addWorker方法
分为2部分,第一部分双重循环找出一个要创建的线程,第二部分创建线程并加入works工作集合。
private boolean addWorker(Runnable firstTask, boolean core) {
/**
双循环找出需要创建的线程对象
**/
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// Check if queue empty only if necessary.
//如果线程池状态是shutdown及以上 或者 shutown此时队列有数据,不创建返回false
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//获取工作线程数
int wc = workerCountOf(c);
//大于核心或者最大线程数,根据参数core判断,返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//cas比较,如:同时有2个线程走到这里,cas成功的跳出for循环在,走到第二步生成新线程加入工作集合;不成功的再循环一次
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);
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;
}
runWorker方法
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
/**
检查线程池是否处于停止状态(STOP)或更高状态,如果是,则确保当前线程被中断。
如果线程池未停止,清除线程的中断状态,但需要重新检查以处理 shutdownNow 调用时可能发生的竞争条件。
**/
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);
}
}
processWorkerExit方法
private void processWorkerExit(Worker w, boolean completedAbruptly) {
/**
处理突然终止的线程:如果 completedAbruptly 为 true,表示线程因异常终止,未调整线程计数,因此调用 decrementWorkerCount 减少线程计数。
**/
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
/**
更新已完成任务数和移除线程:加锁后,增加已完成任务数 completedTaskCount,并将线程从 workers 集合中移除。
**/
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试终止线程池:调用 tryTerminate 检查是否可以终止线程池
tryTerminate();
int c = ctl.get();
/**
判断是否需要补充线程:
如果当前运行状态小于 STOP:
若线程非异常终止,计算最小线程数 min(取决于是否允许核心线程超时)。
如果当前线程数大于等于最小线程数,则无需补充线程。
否则,调用 addWorker 添加新线程。
**/
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);
}
}
tryTerminate方法
尝试更改线程池状态
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 {
/**
CAS则尝试将状态设置为 TIDYING,
调用 terminated 方法,并最终将状态设置为 TERMINATED。
**/
if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
try {
terminated();
} finally {
ctl.set(ctlOf(TERMINATED, 0));
termination.signalAll();
}
return;
}
} finally {
mainLock.unlock();
}
// else retry on failed CAS
}
}
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.
//线程池状态大于0 且 (状态为1 或 队列为空) 线程数-1
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
//工作线程数大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//(工作线程数大于最大线程数 || 第一次(true&&false))
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
//cas减少线程
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;
}
}
}
done ...