美文网首页
线程池的工作原理与源码解读

线程池的工作原理与源码解读

作者: happy518 | 来源:发表于2018-12-24 22:15 被阅读0次

    随着cpu核数越来越多,不可避免的利用多线程技术以充分利用其计算能力,所以多线程技术是服务端开发人员必须掌握的技术。

    随着线程的创建和销毁,都涉及到系统的调用,比较消耗系统资源,所以就引入了线程池技术,避免频繁的创建和销毁线程。

    在java中有一个Executors工具类,可以为我们创建一个线程池,其本质就是new一个ThreadPoolExecutor对象。线程池几乎也是面试题必考的问题。本文结合源码,说说ThreadPoolExecutor的工作原理。

    线程池创建

    先看一下ThreadPoolExecutor参数最全的构造方法:

    public class ThreadPoolExecutor {
      public ThreadPoolExecutor(int corePoolSize, 
                                int maximumPoolSize, 
                                long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, 
                                ThreadFactory threadFactory, 
                                RejectedExceptionHandler handler){
        
      }
    }
    
    1. corePoolSize: 线程池中的核心线程数,说白了就是,即便是线程池里没有任何任务,也会有corePoolSize个线程在候着等任务。
    2. maximumPoolSize: 最大线程数,不管你提交多少任务,线程池里最多工作线程就是maximumPoolSize。
    3. keepAliveTime: 线程的存活时间。当线程池里的线程数大于corePoolSize时,如果登陆keepAliveTime时长还没有任务执行,则线程退出。
    4. unit: 这个是用来指定keepAliveTime的单位,比如秒:TimeUnit.SECONDS。
    5. workQueue: 一个阻塞队列,提交的任务将会被放入到这个队列里。
    6. threadFactory:线程工厂,用来创建线程,主要是为了给线程命名,默认工厂的线程名:pool-1-thread-3。
    7. handler:拒绝策略,当线程池里线程被耗尽了,且队列也满了的时候会调用。

    上面就是创建线程池用到的参数的详细解释。

    线程池执行流程

    这里用一个图来说明线程池的执行过程。

    任务被提交到线程池,会先判断当前线程数量是否小于corePoolSize,如果小于则创建线程来执行提交的任务,否则将任务放入workQueue队列中,如果workQueue满了,则判断当前线程数量是否小于maximumPoolSize,如果小于则创建线程执行任务,否则就会调用handler,以表示线程池拒绝接受任务。

    这里以"1.8.0_171"的源代码为例,看一下具体实现。

    1. 先看一下线程池的execute方法

    ①. 判断当前活跃线程数是否小于corePoolSize,如果小于,则调用addWorker创建线程执行任务

    ②. 如果不小于corePoolSize,则将任务添加到workQueue队列中。

    ③. 如果放入workeQueu失败,则创建线程执行任务,如果创建线程失败(当前线程数不小于maximumPoolSize时),就会调用reject(内部调用handler)拒绝接受任务。

    1. 再看下addWorker的方法实现

    这块代码是在创建非核心线程时,即core为false。判断当前线程数是否大于等于maximumPoolSize,如果大于等于则返回false,即上边说到③中的创建线程失败的情况。

    addWorker方法的下半部分:

    ①. 创建Worker对象,用时也会实例化一个Thread对象。<br />
    ②. 启动这个线程

    1. 再到Woker里面看看其实现

    可以看到在创建Worker时会调用threadFactory来创建一个线程,上面②中启动一个线程就会触发worker的run方法被线程调用。Worker实现了Runable接口。

    1. 接下来看看runWorker方法的逻辑

    线程调用runWorker,会while循环调用getTask方法从workerQueue队列里读取任务,然后执行任务。只要getTask方法不返回null,此线程就不会退出。

    1. 最后在看看getTask方法实现

    ①. 先不管allowCoreThreadTimeOut,这个变量的默认值是false,wc > corePoolSize则是判断当前线程数是否大于corePoolSize。<br />
    ②. 如果当前线程数大于corePoolSize,这回调用wokerQueue的poll方法获取任务,超时时间就是keepAliveTime。如果超过keepAliveTime时长,poll返回了null,上边提到的while循环就会退出,线程也就执行完了,如果当前线程数小于corePoolSize,则会调用wokerQueue的take方法阻塞在当前。

    相关文章

      网友评论

          本文标题:线程池的工作原理与源码解读

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