2.3 ThreadPoolExecutor核心源码:
execute:提交任务
三步曲:
活动线程 < corePoolSize,直接创建新的线程;
活动线程>
corePoolSize,加到任务队列当中;
当队列已满且活动线程< maximumPoolSize,创建新线程,否则拒绝任务
关键设计:
2.3.1 双重校验:在添加队列成功后,还会再次校验线程池是否running状态,若校验失败则从队列移除
2.3.2 若活动线程数量 == 0,添加一个空任务,会重新创建Worker,并且runWorker,真正的目的是runWorker的时候会重新去getTask()即从队列中去获取任务
public void execute(Runnable command) {
…
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) //活动线程数量为0
addWorker(null,false);
}
else if (!addWorker(command, false)) //第三步
reject(command);
}
其中,一二三步意思都很清晰,关键就是第二步有个双重校验:
加入队列成功之后,再次校验线程池状态是否为running,校验不通过则从队列移除,移除成功则拒绝这个任务。校验不通过这其实是临界情况:即加入队列时线程池状态还是RUNNING而双重校验时线程池已经是SHUTDOWN或STOP等。而如果校验不通过并且从队列中移除任务失败了,则实际意义就是已经添加到队列中了,和校验通过是一样的效果,都是把任务添加到队列中了。此时,再判断线程池的活动线程数,若线程池中活动线程数为0,即线程池中没有活动线程,则addWorker(null,
false)。添加一个任务为null的Worker,目的何在?因为线程池中已经没有活动线程了,利用addWorker新开一个线程,而任务为null时,当前线程会从队列中去获取任务。
具体如下:结合下面的addWorker:addWorker成功会new Worker即新开一个线程并启动,start() -> run() -> runWorker() ,runWorker又干了啥,while (task != null || (task = getTask()) != null),这里就很清楚了,task非空的时候,会直接执行当前task。而若task为空会调用getTask() -> workQueue.poll/
workQueue.take,从队列中去获取,执行完再回到循环中重新到队列拉取。所以添加一个null的task目的就是:当活动线程数为空时,会重新启动线程从队列中不断的获取任务,保证队列中的任务都被执行完。
结合上述的一般线程池为SHUTDOWN时,活动线程数才会为空。所以这里针对场景主要就是:添加队列成功后,突然线程池状态变成SHUTDOWN,线程池不再接受新任务,但是仍要把队列中的任务执行完。是不是感觉设计的很精妙?
网友评论