线程池-2

2018-04-20  本文已影响11人  01010100

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,线程池不再接受新任务,但是仍要把队列中的任务执行完。是不是感觉设计的很精妙?

上一篇下一篇

猜你喜欢

热点阅读