有兴趣的一起看下线程池的原理吧。
源码以API-23为例
ThreadPoolExecutor构造方法
线程池的构造方法有好几个,主要分析这个
- corePoolSize,核心线程数;
- maximumPoolSize ,线程池最大线程数,包括核心线程;
- keepAliveTime,线程timeout时间,线程在这个时间内没有任务需要执行将结束,也就是可以资源回收;
- unit,timeout的单位,例如毫秒、秒;
- workQueue,阻塞任务队列,例如LinkedBlockingQueue;
- handler,抛弃策略,也就是当任务大于最大线程数+阻塞队列长度时,新加入任务的处理方式;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
ThreadPoolExecutor.execute 提交任务
public void execute(Runnable command) {
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {//1
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {//2
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))//3
reject(command);
}
- 注释1:workerCountOf获取当前线程数,当它小于corePoolSize时,将触发addWorker方法添加任务;
- 注释2:当前线程数大于等于corePoolSize时,则将任务添加到阻塞队列,等待被执行;
- 注释3:当任务添加阻塞队列失败,那么就执行抛弃策略,也就是构造函数的最后一个参数;
ThreadPoolExecutor.addWorker构建任务
Worker(Runnable firstTask) {//1
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask;
this.thread = getThreadFactory().newThread(this);//2
}
private boolean addWorker(Runnable firstTask, boolean core) {
//省略部分代码
try {
w = new Worker(firstTask);//3
final Thread t = w.thread;
if (t != null) {
......
if (workerAdded) {
t.start();//4
workerStarted = true;
}
}
//省略部分代码
}
return workerStarted;
}
- 注释1: Worker的构造函数只有一个,就是添加的任务;
- 注释2: 创建线程,将Thread与Worker绑定,其实Worker也实现了Runable接口;
- 注释3: 构建Worker,任务作为参数;
- 注释4: 启动线程执行任务,为什么启动线程就能执行任务呢?还记得注释2的绑定关系吗?当start启动线程,就是执行Worker.run,而>Worker.run就是执行任务的run;
线程如何复用呢
//Worker类中
final void runWorker(Worker w) {
//省略部分代码
try {
while (task != null || (task = getTask()) != null) {//1
//省略部分代码
task.run();
//省略部分代码
} finally {
processWorkerExit(w, completedAbruptly);
}
}
- 注释1,getTask()就是获取堵塞队列中的任务执行,只要队列有任务,Thread就一直执行;
线程如何销毁呢
//Worker类中
private Runnable getTask() {
//省略部分代码
for (;;) {
//省略部分代码
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;//1
//省略部分代码
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) ://2
workQueue.take();//3
if (r != null)
return r;//4
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
- 注释1:判断线程是否需要设置超时限制,允许核心线程超时当期线程数大于核心线程数,都应该设置超时限制;
- 注释2,如果需要尝试回收线程,则利用workQueue.poll获取任务;
- 注释3,如果暂时不考虑回收线程,则利用workQueue.take获取任务;
ThreadPoolExecutor线程池几个重要的角色
BlockingQueue<Runnable> workQueue;//阻塞队列,维护任务;
RejectedExecutionHandler handler;//拒绝处理器
Worker worker;//工作线程,对任务的封装
Runnable command:任务的本身
总结
- 当前线程数小于核心线程数时,将创建新线程来执行该任务;
- 当前线程数大于等于核心线程数时,而阻塞队列没有满时,将任务直接加入队列中;
- 当前线程数大于等于核心线程数时,而且阻塞队列满时,将创建新线程来执行任务;
- 阻塞队列满,而且线程数大于等于最大线程数时,将会执行抛弃策略。
以上分析有不对的地方,请指出,互相学习,谢谢哦!
网友评论