美文网首页
线程池解析第二章-线程池源码问题总结

线程池解析第二章-线程池源码问题总结

作者: 不改青铜本色 | 来源:发表于2018-11-13 13:45 被阅读0次

    线程池解析第一章-源码解析
    线程池解析第二章-线程池源码问题总结

    上篇文章分析了关于线程池在源码层面对提交任务的处理方式,但是光光去看源码,有些值得思考的地方可能会被忽略,但是这些点可能是对线程池理解至关重要,理解了才能对线程池有更为整体的认识,本篇文章主要是对阅读源码的一个总结,把一些问题拿出来进行一个总结

    Q1:为什么要使用线程池
    A:当创建一个线程和销毁一个线程所需要的时间大于这个线程的执行时间,那么这就是一个资源上的浪费,当线程创建过多了以后,线程间的切换则会消耗大量的内存和资源,线程池中保存了空闲的线程,可以利用空闲的线程,避免创建销毁的资源浪费

    Q1-0:线程和操作系统及CPU的关系
    A:线程实际上是操作系统的概念,java本身并不能创建线程,而是调用操作系统提供的接口,实现线程的创建,控制,销毁,实际创建的是操作系统线程,在经由操作系统分配到空闲的CPU上

    • 上面两个问题是对于线程最基本的概念,只有理解了线程的由来,才能对多线程有更完整的认识

    Q2:线程池中的核心线程是如何循环利用的
    A:其实这个问题在阅读完源码后就很好理解了,当执行runWorker()方法的时候,首先会去获取任务,然后有一个while语句去判断当前任务是否为空,或者调用getTask()方法去判断结果是否为空而对应的getTask方法则是去循环获取workqueue阻塞队列中的任务,此处实现了核心线程的循环利用,只不过此处需要注意对有,核心线程在默认情况下会一直存在,当核心线程在被allowCoreThreadTimeOut为true后,也是可以被销毁的,但是非核心线程在keepalivetime过期后如果没有获取到任务将会自动被销毁

    Q3:在线程池中,如果线程没有全部执行任务,为什么不是利用闲置的而是创建新的线程呢?
    A:这是我之前代码没读完的时候产生的一个问题,看完代码后这个问题就很简单了,对于核心线程来说,如果队列任务为空,那么这些核心线程就是属于空闲状态,所以核心线程是否处于空闲状态一个很关键的所在就是工作队列中是否有任务,如果想要利用空闲线程,那么任务队列就必须有任务,但如果此刻线程池线程数还没有达到核心线程数,这时候则会创建核心线程,但是当线程数量大于核心线,则会加入到工作队列当中,这时候就成功利用闲置的核心线程,但是不会增加新的线程

    Q4:Set类型的workers又是做什么的,为什么worker会加入到workers当中
    A:workers是当前线程池中所有线程的集合,当有新的线程被创建后,都会被加入到这个集合当中,对于workers的操作,只有在拿到mainLock的时候才能进行访问,在shutdown()等关闭方法中,被用作检测以确保呼叫者可以中断workers集合中的每一个工作线程,并对所有workers中的工作线程进行中断

    Q5:在addWorker方法中,当任务加入任务队列中且线程池中可用线程数量为0时,调用addWorker(null, false)是什么意思,当无法再向任务队列中添加任务时,调用addWorker(command, false)的作用是什么,他和addWorker(command, true)方法有什么区别
    A: 调用addWorker(null, false)的作用,addWorker是为了创建线程处理传递的任务,此处传递的task为空,说明此处创建了一个任务为空的worker对象,目的只有一个,就是处理之前被加入到任务队列中的task。
    addWorker(command, false)和addWorker(command, true)有什么区别,在addWorker方法中,只是简单的做了一个线程数量的判断,但是在getTask方法中,如果是非核心线程,在keepAliveTime内没有获取到任务,将会销毁非核心线程

    • Q2~5主要说的是线程池实现逻辑上的问题,如有不对的地方欢迎指正

    Q6:在线程池当中,submit()/execute()之间关系及区别
    A:线程池的执行入口通常为submit()/execute(),而submit()方法可以返回线程执行的结果,使用execute()方法则不需要返回执行的结果,submit()方法实际是调用了ThreadPoolExecutor父类AbstractExecutorService的submit方法,该方法可以接收两个类型的参数,一个是Runnable对象,一个是callable对象,但是在submit()方法中会调用newTaskFor()返回一个...
    FutureTask对象,在构造FutureTask对象的时候,如果传入的是Runnable类型,也会被转换成Callable类型

    Q7:在线程池当中,runnable和callable的关系及区别
    A:线程池在接受任务的时候,往往接受两种类型,一种是runnable一种是callable类型,runnable类型需要实现run方法,而callable类型则需要实现call方法,在线程池进行提交任务的操作中,通过submit提交是可以事先获取一个futureTask对象的返回值,说明提交到submit中的对象是实现了call方法的,但在源码中submit是可以接受runable对象,这是因为submit在接受到了runnable对象后,会将runnable转换成callable对象,我前面说了,callable对象是需要实现call方法的,但是被转换的runnbale只有run方法,这一步实际上源码的转换类帮助我们重写了call方法,在call方法中嵌套了run方法

    Q8:线程池执行任务类,如果是通过submit方法提交的,那么如何将计算的结果放入futureTask对象当中的
    A:在进行submit方法进行提交任务的时候,实际返回的是一个 RunnableFuture对象,在线程池调用run方法的时候,实际调用的是RunnableFuture接口,对应的实现类是futureTask,在futureTask对应接口RunnableFuture的run方法的实现方法中,则是调用了任务类的call方法,并将获取到的结果存入futureTask对象当中,对于futureTask中的实现逻辑不是很了解的同志可以参考我之前的文章
    Future三重奏第二章:FutureTask源码解析

    • Q6,Q7,Q8这三个模块的问题需要放在一起理解,他不是单独的孤零零的问题,而是综合在一起的,彼此紧密关联的问题点,主要说明的是对于线程池中不同提交方式带来的两者之间的差别和实现结果上的不同,且其中涉及到futureTask类的理解,如果对futureTask不是很清楚,那么对于线程池的实现,在理解上就不能说是特别的通透,建议有兴趣的朋友可以看看我之前写的文章

    Q9:内部类Worker是继承实现了AQS的,目的是什么,体现在什么地方
    A:主要从Worker对创建以及使用上来阐述这个问题,分为以下几点
    1:当线程池创建Worker对象的时候,对state对默认赋值为-1,此处对目的是为了防止线程在执行前被标记中断
    2:在线程池调用关闭操作的时候,首先会获取线程集,依次取出当中每一个线程,当线程还没有执行任务and xxxx 的时候,将会被标记打断,此处注意,如果线程对state为-1,无法被标记为中断
    3:在线程池中执行runWorker方法的时候,首先需要调用w.unlock()方法,调用此方法将当前线程的state置为0,目的是为了防止线程在被调用前就被标记中断,调用此方法后,线程就可以被置为中断状态

    Q10:interrupt,interrupted,isinterrupted的关系和作用
    A:在这里提出这个问题是因为在上面的问题中,当线程加锁后,需要判断当前线程是否需要被中断,在一系列当判断后,将会对线程进行中断操作,所以此处单独提出来看
    1:interrupt的作用是对当前线程进行中断,只能作用在当前线程
    2:interrupted的作用是返回当前线程的中断状态,并清除中断位
    3:isInterrupted的作用就是返回当前线程的中断状态,不做任何操作

    Q11:对于线程池当中执行拒绝策略的解析
    A:当线程池中任务队列数量已满且线程数量超过线程池最大数量的时候,将会执行拒绝策略,拒绝策略有四类,具体执行何种拒绝策略需要视创建线程池时选择的拒绝策略而定,四种拒绝策略会采用四种不同的处理方式,且都为ThreadPoolExecutor内部类,实现了接口RejectedExecutionHandler中的执行方法,建议此处查看相关源码
    1:AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
    2:DiscardPolicy:也是丢弃任务,但是不抛出异常。
    3:DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程),通过e.getQueue().poll()实现拒绝策略
    4:CallerRunsPolicy:由调用线程处理该任务,相关代码:r.run();直接调用线程处理

    诸王少年时

    相关文章

      网友评论

          本文标题:线程池解析第二章-线程池源码问题总结

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