• 线程池ThreadPoolExecutor源码分析(二)


    线程池中的工作线程是如何实现线程复用的?一个线程一般在执行完任务后就结束了,怎么再让他执行下一个任务呢? 

    当我们往线程池添加任务的时候使用ThreadPollExcutor对象的execute(Runnable command)方法来完成的。那我们就来看一下这个逻辑部分的代码:

       public void execute(Runnable command) {
            int c = ctl.get();
            if (workerCountOf(c) < corePoolSize) {
                //1、当前工作线程小于corePoolSize,新建worker线程并返回
                if (addWorker(command, true))
                    return;
                c = ctl.get();
            }
           
            //2、如果当前工作线程大于等于corePoolSize,添加至任务到队列。并进行二次确认(确认队列是否关闭,进行回滚)
            if (isRunning(c) && workQueue.offer(command)) {
                int recheck = ctl.get();
                if (! isRunning(recheck) && remove(command))
                    reject(command);
                else if (workerCountOf(recheck) == 0)
                    addWorker(null, false);
            }
           
            //3、添加队列失败后,则尝试新建非core线程,失败则拒绝任务。
            else if (!addWorker(command, false))
                reject(command);
        }
    

    可以看出:ThreadPoolExecutor.execute()的功能就是:

    • 将任务添加至阻塞队列workQueue:workQueue.offer(command)
    • 根据corePoolSize和maxPool,选择是否创建Worker:addWorker()

    其中我们可以看到核心逻辑是执行addWorker,线程复用的实现应该在worker中,打开addWorker()方法观察:

    private boolean addWorker(Runnable firstTask, boolean core) {
      w = new Worker(firstTask);
      final Thread t = w.thread;
      ...//代码
      t.start();
      ...//代码
    }
    

    上面代码逻辑中新建Worker对象,其实可以猜测到就是新建线程并启动。到Worker类看一下,是一个内部私有类,实现了Runnable接口。在run方面里面只有一句runWorker:

    private final class Worker    extends AbstractQueuedSynchronizer    implements Runnable{.....
        
         public void run() {
                runWorker(this);
        }
    
                                                                                    }
    

    参考了网上一个简化的runWorker(this)方法的写法:

        final void runWorker(Worker w) {
        Runnable task = w.firstTask;
        w.firstTask = null;
        while (task != null || (task = getTask()) != null) {
                try {
                    task.run();
                } finally {
                    task = null;
                }
            }
    }
    

    很显然这个runWorker很不得了,里面一个大大的while循环,当我们的task不为空的时候它就永远在循环,并且会源源不断的从getTask()获取新的任务,继续看getTask()方法:

    //很显然这个方法是从队列中获取任务workQueue
    private Runnable getTask() {
           ...//代码
           // Are workers subject to culling?
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            
            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
    

    分析到这里线程复用的流程就算完了,最核心的一点是:新建一个Worker内部类就会建一个线程,并且会把这个内部类本身传进去当作任务去执行,这个内部类的run方法里实现了一个while循环,当任务队列没有任务时结束这个循环,则这个线程就结束。

    总结:线程重用的核心是,我们知道,Thread.start()只能调用一次,一旦这个调用结束,则该线程就到了stop状态,不能再次调用start。则要达到复用的目的,则必须从Runnable接口的run()方法上入手,可以这样设计这个Runnable.run()方法(就叫外面的run()方法):
    它本质上是个无限循环,跑的过程中不断检查我们是否有新加入的子Runnable对象(就叫内部的runnable:run()吧,它就是用来实现我们自己的任务),有就调一下我们的run(),其实就一个大run()把其它小run()#1,run()#2,...给串联起来了,不停地处理我们提交的Runnable任务,基本原理就这么简单。


    期间参考众多资料,具体有哪些自己也理不清了。如果冒犯,敬请联系~

  • 相关阅读:
    #35 string(缩点+动态规划)
    BZOJ2744 HEOI2012朋友圈(二分图匹配)
    BZOJ2749 HAOI2012外星人(数论)
    BZOJ2743 HEOI2012采花(离线+树状数组)
    洛谷 P3539 [POI2012]ROZ-Fibonacci Representation 解题报告
    关于图论的一些问题模型
    洛谷 P2505 [HAOI2012]道路 解题报告
    ST表
    洛谷 P4754 True Vegetable 解题报告
    洛谷 P2053 [SCOI2007]修车 解题报告
  • 原文地址:https://www.cnblogs.com/simon-1024/p/12217144.html
Copyright © 2020-2023  润新知