点击上方“小罗技术笔记”,关注公众号
第一时间送达实用干货
之前发表一篇文章
一个读者提出了这么个问题,特此写此文来说说线程池是如何实现复用的。

线程重用的核心是,我们知道Thread.start()只能调用一次,一旦这个调用结束,则该线程就到了stop状态,不能再次调用start。那么要达到复用的目的,则必须从Runnable接口的run()方法上入手,那么应该这样设计这个Runnable.run()方法(就叫外面的run()方法):
它本质上是个无限循环,跑的过程中不断检查我们是否有新加入的子Runnable对象(就叫内部的runnable:run()吧,它就是用来实现我们自己的任务),有就调一下我们的run(),其实就一个大run()把其它小run()#1,run()#2,...给串联起来了,基本原理就这么简单 不停地处理我们提交的Runnable任务。
伪代码:
public void run() {
while(true) {
if(tasks available) {
Runnable task = taskqueue.dequeue();
task.run();
} else {
// wait or whatever
}
}
}
大概的实现思路我们粗略的了解了一下,那再分析jdk中是如何实现线程复用的
先看ThreadPoolExecutor.execute()方法
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
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);
}
else if (!addWorker(command, false))
reject(command);
}
分析:可以看出:ThreadPoolExecutor.execute()的功能就是:
1、将任务添加至阻塞队列workQueue,workQueue.offer(command) 2、根据core和maxPool,选择是否创建Worker,addWorker()
so又回到了Worker的run方法上了!
public void run() {
runWorker(this);
}
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
}
}
·······
}
分析:
1、一个大大的while循环,当我们的task不为空的时候它就永远在循环,并且会源源不断的从getTask()获取新的任务。
2、通过task.run();执行具体的任务。
3、正常情况,只有当所有任务执行完毕才会停止运行。
我们进一步分析getTask()和task.run()方法。在getTask() 有这么一个代码
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
可以发现是从workQueue中获取task的,所以最终的问题就是看这个变量workQueue是谁的成员变量。
public class ThreadPoolExecutor extends AbstractExecutorService {
private final BlockingQueue<Runnable> workQueue;
···
}
通过以上逐步分析可以发现其实线程复用最核心的一点是,新建一个Worker内部类就会建一个线程,并且会把这个内部类本身传进去当作任务去执行,这个内部类的run方法里实现了一个while循环,当任务队列没有任务时结束这个循环,则这个线程就结束。
那为啥抛出异常的线程会被移除掉,再创建一个新线程放到线程池呢?
其实很简单,源码里面 while内部的try/catch 会捕获任务的异常记录然后抛出,循环里面出异常了就会结束这个循环了直接走这个worker的结束方法了,而 processWorkerExit中会默认删除线程,此时出异常的线程可能还有任务待处理新建一个线程来补上,若有任务就继续处理剩下的。

长按二维码关注
点个在看再走呗!




