线程池简述ThreadPoolExecutor 设计思想
ThreadPoolExecutor 应用场景
通过线程池的使用减轻小反复创建线程带了的开销,JDK提供的线程池主要有5中,前3种线程池的构造都是通过调用ThreadPoolExecutor的重载构造方式创建,只是在传入的参数上有不同的设置。
1. newFixedThreadPool 固定数量线程池
2. newCachedThreadPool 根据实际情况调整线程数的线程池
3. newSingleThreadExecutor 只有一个线程的线程池
4. newScheduledThreadPool 返回一个定时执行任务的线程池
5. newSingleThreadScheduledExecutor 一个线程数为1的定时执行线程池
对线程池了解之前了解ThreadPoolExecutor的非常重要
需要掌握ThreadPoolExecutor中的构造参数
- corePoolSize 核心线程数:在提交线程任务小于核心线程数的情况下,会通过创建性的线程来执行任务,默认情况下:线程池中线程的数量会稳定在核心线程数指定的数量。
- maximumPoolSize 最大线程数:线程池中的线程数量一定是在最大线程数以下。
- keepAliveTime 空闲线程最大存活时间:出现空闲线程的情况时,当线程数量大于corePoolSize的时候,空闲的线程在等待keepAliveTime时间后会停止运行直到线程数量降至corePoolSize。
- unit 存活时间单位:作为keepAliveTime 的修饰
- workQueue 任务缓冲队列:当前线程数大于等于corePoolSize 的时候,新加入的任务会被放到workQueue中,进行缓存。如果workQueue装满了,就需要执行拒绝策略。
- threadFactory 线程工厂:通过工厂构建的渠道创建线程,默认的效果是执行
new Thread()
。 - handler 拒绝策略:在任务队列装满,同时当前线程数量等于maximumPoolSize,即无法通过扩展线程数来保证任务的执行的情况下,执行相应的拒绝策略。
重要的内部类Worker
Worker实现RunnableWork接口,在ThreadPoolExecutor中代表了着一个执行的线程,所有的Worker放在ThreadPoolExecutor 中的workers集合属性中。Woker的数量代表的就是线程的数量。
Worker的主要任务就是从workQueue 不断的通过getTask()
取任务,在workQueue中有任务的情况下则立即返回队列中的任务task,取出任务task后执行task的run()
方法,否者就会阻塞在getTask()
方法上面(这个地方类似消费者模式)。getTask()
在线程数量超过corePoolSize的情况下是定时等待获取,等待时间为keepAliveTime ,当超过keepAliveTime 会停止等待返回null任务,导致Worker任务跳出循环任务,跳出循环后会进行执行Woker的退出逻辑;在没有超过corePoolSize的情况下执行的是阻塞等待的操作,等待workQueue 放入新的任务然后返回task。
while (task != null || (task = getTask()) != null) {//不断从workQueue中获取任务
//...状态判断逻辑
beforeExecute(wt, task);//自定义任务执行前逻辑
task.run();//将获取的任务执行
afterExecute(task, thrown);//自定义任务执行后逻辑
}
//...
finally {
processWorkerExit(w, completedAbruptly);//执行当前worker线程离开前的任务
}
在创建线程池的时候可以通过重写ThreadPoolExecutor的beforeExecute
方法以及afterExecute
方法来实现任务执行前后的逻辑定义。
任务提交方法execute(Runnable command)
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//当前线程的数量以及当前线程池的状态,都被包装在ctl中。ctl的高位表示状态,低位表示当前线程池的数量
int c = ctl.get();
//当前线程数量小于核心线程数量则创建一个Worker线程执行当先任务
//addWorker(command, true),true表示在核心线程数范围内添加新线程
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//当前线程池RUNNING的情况下,当前线程数量大于核心线程数量,队列未满,向workQueue队列中排队当前任务
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//检查在任务加入队列期间,线程池是否仍然处于运行状态。
//如果线程池已经进入停止运行的状态,拒接才提交的command任务
if (! isRunning(recheck) && remove(command))
reject(command);//执行任务拒绝策略
//如果线程池仍然在运行状态,但是线程池中没有Woker的情况(Woker都因为超时退出了),为保证提交到队列中的command任务被执行,所以创建一个firstTask为null的Worker线程去处理线程池中的任务。
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//当前线程数量大于核心线程数量,由于队列已满,所以尝试创建新的线程,如果添加新的线程导致超出maximumPoolSize,则执行拒绝策略。
else if (!addWorker(command, false))
reject(command);
}
用户调用execute方法向线程池提交一个任务,提交任务时根据当前的ctl
获取当前线程池中线程数量判断是否要创建新的Worker线程来服务,还是将任务添加到任务队列中,或者是通过拒绝策略拒绝此任务,执行过程见代码注释。
Worker线程添加
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);//获取线程池运行状态
// Check if queue empty only if necessary.
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
/**
* 1.保证线程数量不超过最大线程池数量接近Integer
* 2.在通过传入的限制条件,限制是否继续执行添加
*/
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
// 如果当前的运行状态不等于rs,说明状态已被改变,返回第一个for循环继续执行
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
/ 第二步 创建一个Worker,包装当前的任务,并启动该work中创建的线程,用于执行当前当前提交过来的任务
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
w = new Worker(firstTask);//新建一个worker同时从ThreadFactory中创建一个新的线程
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);//放入worker集合
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {//worker添加成功,启动任务
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
首先使用CAS操作保证成功增加workerCount,然后将创建一个worker,将worker添加到workers池,启动worker,返回任务添加成功,启动运行创建好的Worker线程,致run()
方法运行便Woker开始执行firstTask,执行完firstTask任务后从WorkerQueue中取任务执行。