ThreadPool在实际应用中提升响应与执行效率,避免线程在创建,销毁占用jvm的性能。接下来深入探索ThreadPool的底层原理。
jdk提供两个类创建线程池Executors和原生ThreadPoolExecutor,实际Executors在创建线程池调用的还是ThreadPoolExecutor类中的构造器,由于Executors在使用消息入队的队列大小是Integer.MAX_VALUE方式,如果任务多的话极易导致内存泄漏,在实际开发中禁止使用Executors创建线程池,使用ThreadPoolExecutor手动配置合理的参数创建线程池。
ThreadPoolExecutor构造函数的参数说明:
- int corePoolSize,
线程核心数大小,线程池中无任务时候存活的线程个数 - int maximumPoolSize,
非核心线程数与核心线程数总和,线程池中最多可存活的线程个数 - long keepAliveTime,
无任务时候非核心线程数存活的时间 - TimeUnit unit,
无任务时候非核心线程存活时间单位 - BlockingQueue<Runnable> workQueue,
阻塞队列,当核心线程都在工作,又有任务过来先入队列,用于存放执行任务的队列 - ThreadFactory threadFactory,
创建线程的工厂默认使用Executors.DefaultThreadFactory 用于存放 - RejectedExecutionHandler handler
拒绝策略- AbortPolicy:当队列满了,线程池中线程数量已到最大线程数,当有任务提交直接拒绝,并抛出异常
2.CallerRunsPolicy:当队列满了,线程池中线程数量已到最大线程数,当有任务提交由调用线程执行
3.DiscardOldestPolicy:当队列满了,线程池中线程数量已到最大线程数,当有任务提交直接丢弃最老的任务,不抛异常
4.DiscardPolicy:当队列满了,线程池中线程数量已到最大线程数,当有任务提交直接丢弃不抛异常
- AbortPolicy:当队列满了,线程池中线程数量已到最大线程数,当有任务提交直接拒绝,并抛出异常
ThreadPoolExecutor几个重要的属性
1.ctl正在运行的线程数量
2.Worker存放线程对象
启动线程执行任务
有两种提交任务submit和excute,不同在于submit提交会有返回值,excute提交没有返回值(以后在写文章介绍这两种不同点)
execute源码
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//c初始化是-536870912
int c = ctl.get();
//获取正在工作的线程数量
//有任务提交判断正在工作的线程数量是否小于核心线程数
// 如果小于将线程添加到工作队列中启动线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//isRunning(c)为true,
//如果线程池处于RUNNING状态,则添加任务到阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再做一次检查
//如果当前线程池状态不是RUNNING则从队列删除任务,并执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//如果当前线程池线程空,则添加一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//添加到任务队列失败,尝试创建非核心线程如果
//非核心线程已满直接拒绝
else if (!addWorker(command, false))
reject(command);
}
1.判断工作线程是否小于核心线程
如果小于直接创建线程添加到工作对列中(Worker)
2.如果不小于核心线程数
判断线程池是否运行状态,如果是将任务添加到阻塞队列中
如果入队成功做二次检查,线程池非运行状态将任务删除使用拒绝策略
如果当前线程池为空添加一个线程
3如果入队失败尝试创建非核心线程数,创建非核心线程数失败使用拒绝策略
addWorker(Runable,Boolean)方法
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);
//判断工作线程是否大于最大限制
//或者是否大于=核心线程or最大线程数 如果是返回false
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//cas将工作线程个数加1,如果失败重新循环校验,成功将工作线程加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 {
//创建一个Worker将任务和线程赋值给该Worker
w = new Worker(firstTask);
final Thread t = w.thread;
if (t != null) {
//加锁将worker添加到HashSet中
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();
//将创建好的线程和任务的worker添加到hashset中
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
//如果创健worker成功启动当前线程执行任务
if (workerAdded) {
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
addWorker方法主要作用就check检查和创建worker,启动线程执行任务,worker类实现了Runable接口重写了run方法,runWorker方法才是真正处理核心任务,如何线程复用以及当非核心线程空闲时关闭非核心线程。
着重分析runWorker方法
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//获取当前任务
Runnable task = w.firstTask;
//将worker的任务置空,下次取任务从队列取
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//任务不为空 getTask()也是获取任务获取的是队列中的任务,稍后我们详细讲解getTask()
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 {
//将worker从hashset中删除
processWorkerExit(w, completedAbruptly);
}
}
getTask()方法从队列取数据,如果任务没了返回空,核心线程数阻塞在workQueue.take()方法处
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.
//检查队列是否为空,线程池是否停止 如果停止或者为空将工作线程进行-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;
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//如果线程池工作线程数大于corPoolSize使用poll方式后获取任务返回null
//否则线程池数量<=corPoolSize数量进行take()方式获取任务
//如果队列为空则当前线程阻塞在这里
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
ThreadPoolExecutor源码已经分析完毕,进行一个小的总结
总结:
1.任务执行时首先判断当前线程池线程数量是否小于核心线程数,
小于的核心线程就添加到工作worker中将worker添加到HashSet<worker>中,并启动线程执行worker中的任务,执行完之后处于while循环取队列的任务执行。
大于核心线程将任务添加到阻塞队列中,让已启动的核心线程去队列中取任务执行,但是如果队列满了又来新的任务,会创建非核心线程数来执行任务,当核心线程数和非核心线程数总和等于线程池最大线程数进行一个执行拒绝策略。
2.当没有任务提交时,线程池中线程执行队列中的任务,在取任务时候会判断当前线程池中的线程数量是否大于核心线程数如果大于那么线程取队列的任务使用poll+时间参数,如果队列为空了线程取任务超时取不到数据会返回null,接下来执行processWorkerExit移除worker工作对象,线程池保留的是核心线程数,而这些线程阻塞在取队列任务的take()方法处,等待线程将任务offer进队列时候唤醒进行取任务。