一、线程复用
1.简述
使用 new Thread() 创建的线程,再调用 start() ,运行完毕 run() 之后就结束了,而线程池中的线程之所以能够复用,是因为线程池中的工作线程 run() 内部是一个循环,会循环从阻塞队列中获取 Runnable 任务来执行,如果获取不到任务就会被阻塞队列阻塞住,进入空闲状态,等有任务需要线程池处理了,线程会被阻塞队列唤醒,然后处理任务,达到复用的效果。
2.原理分析如下
①.线程池中线程 Worker 的 run 方法是调用的 runWorker 方法
②.runWorker 方法中存在一个循环,在没有任务时【task==null】会尝试获取任务,调用 getTask方法
③.getTask 方法中会从阻塞队列中尝试获取 Runnable 任务而被阻塞,进入空闲状态
二、线程池空闲退出
1.简述
在使用 ThreadPoolExecutor 创建线程池时,会传入最大线程数【maximumPoolSize】、核心线程数【corePoolSize】,默认在空闲时核心线程不会退出,而多出 corePoolSize 的非核心线程会退出,其实通过设置 allowCoreThreadTimeOut 为 true,核心线程在空闲时也会退出,而线程退出的原理就是使用上述结论1中的阻塞队列完成的,线程在获取任务的时候,都会调用一个叫做 getTask的方法,在 getTask 内部,非核心线程获取任务时,调用的是阻塞队列的 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 方法,就是说在指定时间从阻塞队列获取任务数据,如果获取不到,就会导致getTask返回一个 null,然后工作线程就会因为这个返回值而退出循环,就结束了线程的运行,而剩余的核心线程在从阻塞队列中获取任务的时候,调用的是阻塞队列的 workQueue.task(),这个方法是不会超时的,只有获取到任务数据才会停止阻塞,所以核心线程空闲时就是被阻塞了,但不会结束,但如果设置了 allowCoreThreadTimeOut 为 true,那么所有线程都平等了,获取任务时调用的都是 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS),指定时间内没有获取到任务,getTask 都会返回 null,都会退出。
2.原理分析如下:
①.调用 getTask 获取任务时,存在线程数量的判断,默认情况下,当线程数大于核心线程数时,从队列中获取任务的方法就是调用带有超时时间的poll 方法,在超时之后,getTask 返回 null。
【注:以下是非核心线程在空闲时,获取任务超时退出逻辑说明】
private Runnable getTask() {
//1.默认超时标志为 false
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.
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//2.获取工作线程的个数
int wc = workerCountOf(c);
//3.allowCoreThreadTimeOut 这个值默认是 false,表示是否在核心线程空闲时关闭,如果设置 true 的话,那么所有线程
// 从队列中获取任务时,调用的都会是阻塞超时方法 poll
//4.当前工作线程数大于核心线程数时,wc > corePoolSize 为 true, timed 为 true,表示当前线程是非核心线程
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//5.wc > maximumPoolSize 表示当线程数超出了最大线程数限制,需要关闭,所以就直接线程数减一,返回 null
//7.timeOut 变为 true 之后,非核心线程就会走这里的逻辑,方法结束循环,返回 null 用于结束工作线程的运行
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//6.timed 为 true,非核心线程这里会调用 poll 阻塞超时方法获取任务,在指定时间内没有获取到任务的话,timeOut 就会变为 true
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
②.getTask 返回 null 之后,worker 工作线程的循环就会结束,线程就会结束运行进入终止状态