• 11.线程池是怎样执行任务的


    线程池是怎样执行任务的?

     

    我曾经在一个面试中被问到,说说线程池是怎样执行任务的,由于对这个知识点不是很清楚,我当时很紧张,回答的不好,因此面试完当天我就恶补了这个知识点,现在来重温一下说到执行任务,我们就必须先谈谈提交任务。

    提交任务的方式有两种,一种是execute,还有一种是submit。

    这两种方式在前面的章节已经讲过,它们的区别在于execute只能提交runnable任务,也就是无返回之任务。而submit既可以提交runnable部任务,也可以提交callable任务,也就是有返回值的任务。虽然两者提交任务的种类有差异,但是他们最终处理任务的方法是相同的,都是threadpoolExecutor类的execute方法。

    Execute方法的实现就在 threadpoolExecutor内中submit方法的实现,是在attributeexecuteService抽象类,

    看看submit方法内部做了哪些操作,首先判断任务是否为空,当任务为空时,抛一个空指针异常,当任务不为空,将任务转为有返回值任务。然后调用 execute方法执行,该任务此处调用的是threadpoolExecutor类中的execute方法,最后返回future对象拿到结果,调用者再通过future对象拿到执行结果。

    接下来重点讲讲execute方法。execute方法可分为三个部分来看,第一部分判断核心线程数是否已满,能否再加入新的核心线程。第二部分判断任务队列是否已满,能否再加入新的任务。第三部分判断线程池中的线程是否已满,能否再加入新的线程。 

    接下来以此来看这三个部分。

    首先来看第一部分。

    判断核心限值数是否已满,也就是这行代码workaccountof方法的作用是获取当年线程池中有多少线程,corepoolsize的属性记录了当前限制池中和多少核心线程,当池中的限制数小于核心限值数,也就是核心线程没满时,

    添加核心线程并执行提交的任务,也就是这行代码addworker方法的作用是往线程池中添加线程,该方法有两个参数,第一个参数是指定给线程的任务,在线程刚创建的时候就可以指定任务,线程拿到任务以后就开始执行。第二个参数是指定该线程是否为核心线程,true代表是核心线程。false代表不是核心线程,我们知道成为核心线程就意味着即使空闲下来也不会被销毁。 

    当然单独设置允许销毁核心线程除外,addworker方法\我们就简单的说完了。

    线程添加成功会怎样?线程添加成功,执行完任务这个流程就结束了,直接return这是添加成功的情况。

    假设添加失败,也就是核心线程以满,继续往下执行。

    Ctl记录着线程池状态和池中有多少线程,这里获取它的值是为后续流程做准备。

    接下来看第二部分,

    当核心线程已满时,我们就要考虑将任务先放入任务队列中,此时要判断一下任务队列是否已满,isrunning判断线程池是否还在运行,如果没在运行,好,直接跳过这个流程,进入下一个流程。如果线程池还在运行,

    将任务添加至任务队列中,

    也就是调用workerqueue的offer方法,如果添加成功,任务将在队列中等待被执行,看见流程图是怎样的,任务队列没满,任务添加成功,等待被执行,这就是队列没满的情况。 

    来看左边的代码部分,if语句里面还有任务添加后的操作,再次获取线程池状态,

    检查线程池是否还在运行,如果没运行就从队列中移除任务,然后拒绝任务,

    如果线程池还在运行,那么就检查池中的线程数量是否为0,也就是池中已经没有可用的线程,那么就添加一个非核心线程。

    第二部分看完再来看

    第三部分,

    当任务队列已满时,我们就需要考虑往线程池中添加线程的,

    如果池中的线程没满,那么就直接添加线程并执行提交的任务,这是一种结果。

     

    还有一种结果是当池中的线程已满,无法再往里面添加新线程时,我们只有将任务拒绝掉,

    这是第三部分看完。我们已经画完整个流程图,也已看完代码执行过程,应该来说代码搭配着流程图一起看,这个过程理解起来更容易。 


    总结

    最后总结一下本节内容,本节介绍了线程池执行任务的流程,如图所示,以后在面试中遇到类似的问题就可以轻松应对了,甚至只看execute的方法,源代码能否在纸上将其流程图画出来,看自己掌握了多少流程图。 

     

      

    附录:

    笔记完整文本:

    我曾经在一个面试中被问到,说说线程池是怎样执行任务的,由于对这个知识点不是很清楚,我当时很紧张,回答的不好,因此面试完当天我就恶补了这个知识点,现在来重温一下说到执行任务,我们就必须先谈谈提交任务,提交任务的方式有两种,一种是execute,还有一种是submit。这两种方式在前面的章节已经讲过,它们的区别在于execute只能提交runnable任务,也就是无返回之任务。而submit既可以提交runnable部任务,也可以提交callable任务,也就是有返回值的任务。虽然两者提交任务的种类有差异,但是他们最终处理任务的方法是相同的,都是threadpoolExecutor类的execute方法。 Execute方法的实现就在 threadpoolExecutor内中submit方法的实现,是在attributeexecuteService抽象类,看看submit方法内部做了哪些操作,首先判断任务是否为空,当任务为空时,抛一个空指针异常,当任务不为空,将任务转为有返回值任务。然后调用 execute方法执行,该任务此处调用的是threadpoolExecutor类中的execute方法,最后返回future对象拿到结果,调用者再通过future对象拿到执行结果。接下来重点讲讲execute方法。execute方法可分为三个部分来看,第一部分判断核心线程数是否已满,能否再加入新的核心线程。第二部分判断任务队列是否已满,能否再加入新的任务。第三部分判断线程池中的线程是否已满,能否再加入新的线程。 接下来以此来看这三个部分。首先来看第一部分。判断核心限值数是否已满,也就是这行代码workaccountof方法的作用是获取当年线程池中有多少线程,corepoolsize的属性记录了当前限制池中和多少核心线程,当池中的限制数小于核心限值数,也就是核心线程没满时,添加核心线程并执行提交的任务,也就是这行代码addworker方法的作用是往线程池中添加线程,该方法有两个参数,第一个参数是指定给线程的任务,在线程刚创建的时候就可以指定任务,线程拿到任务以后就开始执行。第二个参数是指定该线程是否为核心线程,true代表是核心线程。false代表不是核心线程,我们知道成为核心线程就意味着即使空闲下来也不会被销毁。 当然单独设置允许销毁核心线程除外,addworker方法\我们就简单的说完了,线程添加成功会怎样?线程添加成功,执行完任务这个流程就结束了,直接return这是添加成功的情况。假设添加失败,也就是核心线程以满,继续往下执行。Ctl记录着线程池状态和池中有多少线程,这里获取它的值是为后续流程做准备。接下来看第二部分,当核心线程已满时,我们就要考虑将任务先放入任务队列中,此时要判断一下任务作业是否已满,isrunning判断线程池是否还在运行,如果没在运行,好,直接跳过这个流程,进入下一个流程。如果线程值还在运行,将任务添加至任务队列中,也就是调用workerqueue的offer方法,如果添加成功,任务将在队列中等待被执行,看见流程图是怎样的,任务队列没满,任务添加成功,等待被执行,这就是对你没满的情况。 来看左边的代码部分,if语句里面还有任务添加后的操作,再次获取线程池状态,检查线程池是否还在运行,如果没运行就从队列中移除任务,然后拒绝任务,如果线程池还在运行,那么就检查池中的线程数量是否为0,也就是池中已经没有可用的线程,那么就添加一个非核心线程。第二部分看完再来看第三部分,当任务对联已满时,我们就需要考虑往县城池中添加线程的,如果池中的线程没满,那么就直接添加线程并执行提交的任务,这是一种结果。还有一种结果是当池中的线程已满,无法再往里面添加新线程时,我们只有将任务拒绝掉,这是第三部分看完。我们已经画完整个流程图,也已看完代码执行过程,应该来说代码搭配着流程图一起看,这个过程理解起来更容易。 最后总结一下本节内容,本节介绍了线程池执行任务的流程,如图所示,以后在面试中遇到类似的问题就可以轻松应对了,甚至只看execute的方法,源代码能否在纸上将其流程图画出来,看自己掌握了多少流程图。

     

  • 相关阅读:
    Search in Rotated Sorted Array
    Search insert position
    二分法感悟
    The Smallest Difference
    Lintcode: Nuts & Bolts Problem
    167. Two Sum II
    登录页面
    注册页面
    在线版简易计算器
    简单工厂模式
  • 原文地址:https://www.cnblogs.com/cj8357475/p/16031655.html
Copyright © 2020-2023  润新知