线程池是面试的频繁问题,今天我们先看看线程池的基本原理
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池基本原理图,流程如下
下面我们看看线程池的源码,前方高能请耐心看完
任务提交
任务池提交有两种方式,execute和submit,但是底层都是execute,如下源码
public void execute(Runnable command) {
//1.先对command进行校验,如果为空就抛出NPE异常
if (command == null)
throw new NullPointerException();
//2.获取的线程上下文c,这个c存储线程状态和线程个数,一个int存储两位数,
// 高3位表示线程状态,后29位表示线程个数
int c = ctl.get();
//3.判断线程池个数是否小于corePoolSize,如果是,调用addWork新建一个线程
// 新的线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//4.如果线程池处于运行中,那么就加入阻塞队列
if (isRunning(c) && workQueue.offer(command)) {
//5.这里重新获取状态,因为加入队列的同时线程池的转态可能改变
int recheck = ctl.get();
//6.如果线程池不是运行中,就把任务从线程池删除,然后执行拒绝策略
if (! isRunning(recheck) && remove(command))
reject(command);
//7.如果线程池的线程为0,则新建一个线程
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//8.如果添加阻塞队列失败,说明队列满了,如果线程小于maximumPoolSize
// 新建线程,如果大于maximumPoolSize就需要执行拒绝策略
else if (!addWorker(command, false))
reject(command);
}
worker线程获取任务执行流程
我们先看看addWorker方法
首先我们明确的是addwork就是进行新建线程,启动之后,然后执行任务,其他代码我先不要关注,只要关注我画出的框
第一处,声明构造worker线程,并把持有的thread成员变量的应用,赋值给final修饰的Thread e临时变量,
第二处,判断线程是否存活,如果存活就跑异常,因为存活的,就不需要在启动了,然后添加到工作线程集合中,是一个Hashset集合,并设置WorkAdded变量为true
第三处,就是把第一处的Thread t变量启动,也就是Worke内部持有Thread变量
其次我们看看Worker的构造函数和Worker类
private final class Worker
extends AbstractQueuedSynchronizer
implements Runnable
Worker(Runnable firstTask) {
setState(-1); // inhibit interrupts until runWorker
this.firstTask = firstTask; //执行的任务
this.thread = getThreadFactory().newThread(this);//对应的线程
}
看到这里,我就可以联想到上面的addwork方法里面work线程的启动就是启动一个线程,其中firstTask就是执行的任务,然而最终的任务也是调用run方法执行具体的业务
最后看看runWorker方法
我依然重点看我画出来的地方
第一处,就是取出worker中的task应用复制给了Runable变量
第二处,如果变量task为空,就会从队列取出任务,
第三处,然后执行任务,task.run().
最后整体的流程如下如
当我们向线程池提交任务的时候,通过调用execute执行任务(runable或者Callable实现),最终会通过Worker的构造函数传递到worker内部,这样当start启动的以后真正执行的就是worker中的Runable,也就是用户提交的Runbale
Worke线程的退出时机
对于核心线程来说,他们可以无限制的等待任务被获取执行,非核心线程则是在有限时间内获取任务,一旦worker无法获取到任务,也就是任务队列为空,玄幻就会结束,worker自己会主动去除掉在线程池中的应用,进而被回收掉并退出,
再把之前的代码拿出来看看最后几句
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 {
...省略代码
} finally {
processWorkerExit(w, completedAbruptly);
}
}
其实在try代码里面有一个while循环结束条件就是当task==null或者队列里面为空,结束之后就会到finally执行退出代码,我在看看退出代码
上面的代码的核心流程如下图
持续关注,如果对您有一丝丝帮助,麻烦点个关注,也欢迎转发,谢谢