zoukankan      html  css  js  c++  java
  • 线程池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任务,基本原理就这么简单。


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

  • 相关阅读:
    maven常用命令介绍(持续更新)
    JQ笔记
    AspNetPager学习使用2
    AspNetPager学习使用1
    VS2012添加ADO实体数据模型
    手动拍摄0008
    程序自动拍摄0007
    曝光补偿0006
    白平衡0005
    感光度0004
  • 原文地址:https://www.cnblogs.com/simon-1024/p/12217144.html
Copyright © 2011-2022 走看看