参考
https://www.cnblogs.com/xiaxj/p/14275671.html
https://www.cnblogs.com/superfj/p/7544971.html
线程池的使用
如何选择线程池数量
线程池的大小决定着系统的性能,过大或者过小的线程池数量都无法发挥最优的系统性能。
当然线程池的大小也不需要做的太过于精确,只需要避免过大和过小的情况。一般来说,确定线程池的大小需要考虑CPU的数量,内存大小,任务是计算密集型还是IO密集型等因素
NCPU = CPU的数量
UCPU = 期望对CPU的使用率 0 ≤ UCPU ≤ 1
W/C = 等待时间与计算时间的比率
如果希望处理器达到理想的使用率,那么线程池的最优大小为:
线程池大小=NCPU *UCPU(1+W/C)
在Java中使用
int ncpus = Runtime.getRuntime().availableProcessors();
获取CPU的数量。
线程池
ctl
-
是一个AtomicInteger类型
-
一个Integer包含了两个具有实际意义的数:有效的线程数()、运行状态
-
运行状态:
1、RUNNING。 接收新任务并处理排队的任务
2、SHUTDOWN。不接受新任务,但还是会处理队列中的任务
3、STOP。 不接受新任务,也不处理排队中的任务,并且会中断当前在执行的任务。
4、TIDYING。所有任务都已终止,workerCount为零,线程转换为状态TIDYING ,将运行Terminated()挂钩方法。
5、TERMINATED。 执行完terminated()方法
-
* RUNNING -> SHUTDOWN * On invocation of shutdown(), perhaps implicitly in finalize() * (RUNNING or SHUTDOWN) -> STOP * On invocation of shutdownNow() * SHUTDOWN -> TIDYING * When both queue and pool are empty * STOP -> TIDYING * When pool is empty * TIDYING -> TERMINATED * When the terminated() hook method has completed
execute
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();
// 对线程池状态做二次检查,如果此时线程池又不是运行态了,则将任务remove,并执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 在线程池里增加一个线程。
// 主要是为了防止线程池的任务队列里有任务而没有线程可用的这种情况发生。
// 如果没有这个调用。在你把coreSize设置为0时,往线程池里添加任务,任务会被放在任务队列了,永远得不到执行。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 当阻塞队列满时,添加非核心线程。
else if (!addWorker(command, false))
reject(command);
}
-
若当前线程池线程数小于核心线程数,则开启一个新的线程,并将该command作为线程的第一个任务,
-
若当前线程池线程数大于核心线程数,则尝试将任务放入队列
- 若放入成功,则在阻塞队列中排队被消费
- 若放入失败,阻塞队列已满,则使用非核心线程去执行该任务。 addWorker()方法会尝试开启一个非核心线程去执行该任务,并在这之前判断线程数是否已经超过了线程总数,若超过,则返回false,执行拒绝策略。‘
-
为什么要recheck: 因为ctl.get()方法不是原子操作,可能在此时线程池状态已经改变。
假设此时线程池状态已经不是RUNNING状态,而又将任务放入了队列,则会导致该任务永远留在了队列中,也没有任何报错去让程序感知。 而recheck就可以获取当下的线程池状态,从而执行拒绝策略。
workerCountOf
private static int workerCountOf(int c) { return c & CAPACITY; }
CAPACITY是低29位全1,高3位全0, 做与运算就可以得到workerCount。
addWorker
-
SHUTDOW的值为0,RUNNING值小于0,其他的STOP、TIDYING、TERMINATED值大于0。
-
可以分为两个部分理解: ① 校验是否需要增加线程,cas操作ctl,将线程数+1 ② 新建一个线程并启用
-
首先要知道addWorker到底是做什么的,否则下面一些判断逻辑很难弄懂。
addWorker()有三种用法:
-
addWorker(paramRunnable, true):
生成一个有firstTask的线程,且长度限制为corePoolSize。可以看做是产生核心线程的方法。
-
addWorker(paramRunnable, false)
生成一个有firstTask的线程,且长度限制为maximumPoolSize。可以看做是产生非核心线程的方法。
-
addWorker(null, false):
生成一个空闲线程(没有firstTask), 长度限制为maximumPoolSize。 这个要好好解释一下:
在execute()方法中使用到了这种addWorker方法,举个例子,如果corePoolSize为0,而阻塞队列为无限大,这时如果没有这个调用,则任务永远得不到执行。 如果corePoolSize为0,且阻塞队列为无限大,则线程池中只会有一个线程。
if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); // 对线程池状态做二次检查,如果此时线程池又不是运行态了,则将任务remove,并执行拒绝策略 if (! isRunning(recheck) && remove(command)) reject(command); // 在线程池里增加一个线程。 // 主要是为了防止线程池的任务队列里有任务而没有线程可用的这种情况发生。 // 如果没有这个调用。在你把coreSize设置为0时,往线程池里添加任务,任务会被放在任务队列了,永远得不到执行。 else if (workerCountOf(recheck) == 0) addWorker(null, false)
-
-
执行流程
- 判断线程池当前是否为可以添加worker线程的状态,可以则继续下一步,不可以return false:
A、线程池状态>shutdown,可能为stop、tidying、terminated,不能添加worker线程
B、线程池状态shutdown,firstTask不为空,不能添加worker线程,因为shutdown状态的线程池不接收新任务
C、线程池状态shutdown,firstTask==null,workQueue为空,不能添加worker线程,因为firstTask为空是为了添加一个没有任务的线程再从workQueue获取task,而workQueue为空,说明添加无任务线程已经没有意义 - 线程池当前线程数量是否超过上限(corePoolSize 或 maximumPoolSize),超过了return false,没超过则对workerCount+1,继续下一步
- 在线程池的ReentrantLock保证下,向Workers Set中添加新创建的worker实例,添加完成后解锁,并启动worker线程,如果这一切都成功了,return true,如果添加worker入Set失败或启动失败,调用addWorkerFailed()逻辑
- 判断线程池当前是否为可以添加worker线程的状态,可以则继续下一步,不可以return false:
private boolean addWorker(Runnable firstTask, boolean core) {
----------------------第一部分----------------------------------
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 这里不太好理解,可以按以下的来理解
// 1、当线程池状态为RUNNING时,继续往下走。
// 2、当线程池状态为SHUTDOWN,firstTask为空,且任务队列不为空时,也继续往下走,
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;
// cas增加workCount,成功则退出循环,失败则继续
if (compareAndIncrementWorkerCount(c))
break retry;
// 重新获取状态
c = ctl.get(); // Re-read ctl
// 如果状态发生改变,则回到retry外层循环
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;
// 加锁的目的是为了保证workers线程集合的安全
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
int rs = runStateOf(ctl.get());
// 如果线程池状态为RUNNING 或者 SHUTDOWN时添加空线程
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;
}
shutdown
Worker
- 继承了AQS,并实现了Runnable接口
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
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
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);
}
}