线程池ThreadPoolExecutor源码分析(二)
线程池中的工作线程是如何实现线程复用的?一个线程一般在执行完任务后就结束了,怎么再让他执行下一个任务呢?
当我们往线程池添加任务的时候使用ThreadPollExcutor对象的execute(Runnable command)方法来完成的。那我们就来看一下这个逻辑部分的代码:
public void execute(Runnable command) {
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
//1、当前工作线程小于corePoolSize,新建worker线程并返回
if (addWorker(command, true))
return;
c = ctl.get();
}
//2、如果当前工作线程大于等于corePoolSize,添加至任务到队列。并进行二次确认(确认队列是否关闭,进行回滚)
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3、添加队列失败后,则尝试新建非core线程,失败则拒绝任务。
else if (!addWorker(command, false))
reject(command);
}
可以看出:ThreadPoolExecutor.execute()的功能就是:
- 将任务添加至阻塞队列workQueue:workQueue.offer(command)
- 根据corePoolSize和maxPool,选择是否创建Worker:addWorker()
其中我们可以看到核心逻辑是执行addWorker,线程复用的实现应该在worker中,打开addWorker()方法观察:
private boolean addWorker(Runnable firstTask, boolean core) {
w = new Worker(firstTask);
final Thread t = w.thread;
...//代码
t.start();
...//代码
}
上面代码逻辑中新建Worker对象,其实可以猜测到就是新建线程并启动。到Worker类看一下,是一个内部私有类,实现了Runnable接口。在run方面里面只有一句runWorker:
private final class Worker extends AbstractQueuedSynchronizer implements Runnable{.....
public void run() {
runWorker(this);
}
}
参考了网上一个简化的runWorker(this)方法的写法:
final void runWorker(Worker w) {
Runnable task = w.firstTask;
w.firstTask = null;
while (task != null || (task = getTask()) != null) {
try {
task.run();
} finally {
task = null;
}
}
}
很显然这个runWorker很不得了,里面一个大大的while循环,当我们的task不为空的时候它就永远在循环,并且会源源不断的从getTask()获取新的任务,继续看getTask()方法:
//很显然这个方法是从队列中获取任务workQueue
private Runnable getTask() {
...//代码
// 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 {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
分析到这里线程复用的流程就算完了,最核心的一点是:新建一个Worker内部类就会建一个线程,并且会把这个内部类本身传进去当作任务去执行,这个内部类的run方法里实现了一个while循环,当任务队列没有任务时结束这个循环,则这个线程就结束。
总结:线程重用的核心是,我们知道,Thread.start()只能调用一次,一旦这个调用结束,则该线程就到了stop状态,不能再次调用start。则要达到复用的目的,则必须从Runnable接口的run()方法上入手,可以这样设计这个Runnable.run()方法(就叫外面的run()方法):
它本质上是个无限循环,跑的过程中不断检查我们是否有新加入的子Runnable对象(就叫内部的runnable:run()吧,它就是用来实现我们自己的任务),有就调一下我们的run(),其实就一个大run()把其它小run()#1,run()#2,...给串联起来了,不停地处理我们提交的Runnable任务,基本原理就这么简单。
期间参考众多资料,具体有哪些自己也理不清了。如果冒犯,敬请联系~