文章目录
一、线程池产生的原因
如果需要处理大量任务,频繁地创建和销毁线程会浪费时间和效率,尤其是浪费内存。为了让线程重复利用,让它们继续执行其他任务而不是立即销毁,线程池应运而生
二、线程池核心线程数与最大线程数的设置
2.1.、CPU密集型任务的线程数设置
CPU密集型任务主要是针对=>线程在执行任务时会一直利用CPU,所以对于这种情况,就尽可能避免发生线程上下文切换,对于CPU密集型任务,线程数最好等于CPU核心数,只不过,为了应对线程执行过程发生缺页中断或其他异常导致线程阻塞的请求,我们可以额外在多设置一个线程,这样当某个线程暂时不需要CPU时,可以有替补线程来继续利用CPU。所以,对于CPU密集型任务,我们可以设置线程数为:CPU核心数+1
2.2、非CPU密集型任务的线程数设置(IO密集型)
线程在执行IO型任务时,可能大部分时间都阻塞在IO上,假如现在有10个CPU,如果我们只设置了10个线程来执行IO型任务,那么很有可能这10个线程都阻塞在了IO上,这样这10个CPU就都没活干了,所以,对于IO型任务,我们通常会设置线程数为:2*CPU核心数,不过,就算是设置为了2*CPU核心数,也不一定是最佳的,比如,有10个CPU,线程数为20,那么也有可能这20个线程同时阻塞在了IO上,所以可以再增加线程,从而去压榨CPU的利用率:
通常,如果IO型任务执行的时间越长,那么同时阻塞在IO上的线程就可能越多,我们就可以设置更多的线程,但是,线程肯定不是越多越好,我们可以通过以下这个公式来进行计算:线程数 = CPU核心数 乘于( 1 + 线程等待时间 / 线程运行总时间 )
- 线程等待时间:指的就是线程没有使用CPU的时间,比如阻塞在了IO
- 线程运行总时间:指的是线程执行完某个任务的总时间
2.3、小结:
- CPU密集型任务:CPU核心数+1,这样既能充分利用CPU,也不至于有太多的上下文切换成本
- IO型任务:建议压测,或者先用公式计算出一个理论值(理论值通常都比较小)
- 对于核心业务(访问频率高),可以把核心线程数设置为我们压测出来的结果,最大线程数可以等于核心线程数,或者大一点点,比如:我们压测时可能会发现500个线程最佳,但是600个线程时也还行,此时600就可以为最大线程数
- 对于非核心业务(访问频率不高),核心线程数可以比较小,避免操作系统去维护不必要的线程,最大线程数可以设置为我们计算或压测出来的结果。
三、线程池的执行流程底层实现
3.1、线程池内部模型图
由上图,在线程池中,主要包含了核心线程数、最大线程数、线程工厂、线程最大存活时间与时间单位、任务队列和拒绝策略
3.2、线程池任务执行流程
-
线程在接受到任务后,首先是先判断当前线程数是否小于核心线程数的,如果是小于的,就会直接创建线程执行当前任务(JUC包下的线程池,Tomcat中稍作改动==>如果当前核心线程数有空闲的,会将任务直接加入到阻塞队列中,如果当前线程数没有达到最大线程池,就会直接创建线程执行当前任务)
如丧如上图,会调addWorker方法新建线程,如何新建的,我们下面会有介绍,我们先看主干的底层代码流程是如何的 -
其次,如果不满足上一个条件,就会尝试的将任务加入到任务队列中
-
最后是阶段是,如果上一步的加入队列不成功,就会尝试的再去新建新的线程执行任务,如果这一阶段还是不能新建线程,就会执行拒绝策略:
3.3、线程池状态
线程池中主要有五个状态,分别是:
- Runing:表示当前线程是可用的,可用接受任务并处理任务和任务队列中任务
- shutDown:不会再接受新任务,但是会将当前任务队列中的任务处理完
- Stop:不会接受新任务,也不会处理任务队列中的任务,而且还会直接中断正在处理的任务
- Tidying:所有的任务都终止了,线程中,没有线程,这种情况就会将线程状态改为tidying
- Terminated:terminated()执行完之后线程池就会转变为Terminated
3.4、深入剖析线程池底层源码
这里我们剖析线程池源码,主要是从几个方面进行剖析,分别是:线程池中线程创建的过程底层实现机制、线程执行任务的底层实现原理、线程销毁的底层实现机制
3.4.1、线程池中线程创建的过程底层实现机制
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
//1、获取当前线程状态与数量信息
int c = ctl.get();
int rs = runStateOf(c);
// 2、判断当前线程池是否为shutDown以上的(stop、tidying、terminated)
//状态以及如果是shutDown的情况需要先处理队列中任务
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
//3、获取当前的线程数,判断当前的线程数是否大于峰值或者=>
//结合传入的核心线程数标识获取到对应的核心线程数或者最大线程数据
int wc = workerCountOf(c);
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//这里就进行CAS操作,将线程池中的线程+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 {
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();
// private final HashSet<Worker> workers = new HashSet<Worker>();将当前线程加入到结合中
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)
//==> workers.remove(w) 添加到集合中线程需移除; decrementWorkerCount()线程数减1操作;
// tryTerminate()更改状态;
addWorkerFailed(w);
}
return workerStarted;
}
3.4.2、线程执行任务的底层实现原理
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
//在线程池中,其锁从写了,state的值主要是有三个,分别是-1(初始化默认赋值),0(无锁状态),1(加锁状态)
w.unlock(); // allow interrupts-->也就是说初始化时,是不允许中断的,需要调用unlock将state的值改为0,让其可以中断
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(stop、tidying、terminated)并且线程还未中断,需要进行中断处理
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
//任务执行前的方法处理。类似AOP机制
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 {
//任务执行后的方法处理。类似AOP机制
afterExecute(task, thrown);
}
} finally {
task = null;
//任务执行统计数据+1
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
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.
//校验当前线程池的状态情况,如果是要关闭了,且若状态为shutdown,且任务队列为空了,就不接受新任务了
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
int wc = workerCountOf(c);
// Are workers subject to culling?
//allowCoreThreadTimeOut 参数是传递的,表示核心线程数超时时间设置神效是否,获取到当前线程池中的线程数,是否大于核心线程数
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//线程数大于最大线程数或阻塞超时且timed,并且当前线程数是大于1的或任务队列为空的,就将线程池中的线程数减1,返回null之后,外层会执行processWorkerExit方法进行线程回收处理
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
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;
}
}
}
3.4.3、线程销毁的底层实现机制
private void processWorkerExit(Worker w, boolean completedAbruptly) {
//线程退出是否正常,不正常的线程数需要减1操作
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
completedTaskCount += w.completedTasks;
workers.remove(w);
} finally {
mainLock.unlock();
}
//尝试更改线程池状态
tryTerminate();
//获取当前线程池状态与线程数量,如果是比stop值小的(shutDown、Runing),需要半段是否是正常结束。
//正常结束的,需要判断是否需要留线程处理任务队列中的任务数据
int c = ctl.get();
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);
}
}
四、线程池中,线程的退出方式与底层实现
4.1、当前线程数大于核心线程数
如下图:线程数大于核心线程数后,会将标识timed变为true,如果在队列中阻塞超时,没有任务可执行,那除了核心线程数外的多余的线程,就会被回收
4.2、任务执行时抛出异常
如下图:在捕获异常后,最后同样会执行finally语句块,这块代码中的processWorkerExit方法就会判断当前线程是否是正常结束的,也就是completedAbruptly标识,false标识正常结束,反之为非正常退出==>出现异常
4.3、线程池执行shutdown或shutdownnow方法
调用者两个方法后,会将线程池的状态改为如下状态:
改为这个状态后,不管是创建线程还是执行任务,都需要先对线程池状态进行判断的,那就是说,状态变后,会结合当前线程池状态情况,中断线程或者直接中断所有执行的任务