美文网首页
线程池-2

线程池-2

作者: 01010100 | 来源:发表于2018-04-20 11:28 被阅读11次

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

    相关文章

      网友评论

          本文标题:线程池-2

          本文链接:https://www.haomeiwen.com/subject/jxyakftx.html