美文网首页
线程池笔记

线程池笔记

作者: zmy26 | 来源:发表于2022-06-11 12:15 被阅读0次

    前言:线程池相关的所有类都在java.util.concurrent包下面

    一、线程池的继承关系:

    二、重点介绍ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,

                              int maximumPoolSize,

                              long keepAliveTime,

                              TimeUnit unit,

                              BlockingQueue workQueue,

                              ThreadFactory threadFactory,

                              RejectedExecutionHandler handler) {

        xxx

    }

    1、线程池工作原理:

    a、提交优先级:核心线程数 > 工作队列 > 非核心线程数

         创建线程达到核心线程数后,再来任务会放入工作队列中,当工作队列也满了,再来任务会依据核心线程数的数量创建线程,当非核心线程数也满了就会执行拒绝策略

    b、执行优先级:核心线程数 > 非核心线程数 > 工作队列

         执行线程任务时,优先执行核心线程数的任务,其次非核心线程创建的任务,最后才是工作队列中等待的任务

    2、重点解释一下ThreadPoolExecutor构造函数的几个参数,对理解线程池非常重要

    (1)、int corePoolSize:核心线程数,也就是线程池创建后会一直保存几个线程

    (2)、int maximumPoolSize:最大线程数,最大线程数 = 核心线程数 + 非核心线程数(没有这个参数,但在源码中存在的概念)

    (3)、long keepAliveTime:非核心线程数存活的时间

    (4)、TimeUnit unit:非核心线程数存活时间的单位,例如毫秒,秒等

    (5)、BlockingQueue workQueue:工作队列

    (6)、ThreadFactory threadFactory:创建线程的工厂,一般用默认

    (7)、RejectedExecutionHandler handler:拒绝策略,当核心线程数满了,工作队列满了,非核心线程数也满了,此时再有业务调用线程池创建线程时的策略,比如策略可以设置抛异常,可以设置哪个线程调用线程池执行任务哪个线程处理,可以设置丢弃最后一次任务等。

    三、常用创建线程池的方法,在这里涉及到一个设计模式工厂模式,通过调用Executors来创建线程池,而不是直接new线程池(值得学习):

    1、Executors.newCachedThreadPool():这种方式创建的线程池我们进源码看下

    核心线程数0:表示线程池创建时不创建任何线程

    最大线程数Integer.MAX_VALUE即21亿多:表示非核心线程数趋近于无限

    非核心线程数存活时间60:

    非核心线程数存活时间单位秒:表示创建出来的线程60s内没有被使用,就自动销毁

    工作队列SynchronousQueue同步队列:表示队列中只有一个位置,只会有一个任务在等待,其他任务会被创建线程池立马执行

    缺点:由于创建线程数趋近于无限大,会造成cpu100%

    2、Executors.newFixedThreadPool(5):这种方式创建的线程池我们进源码看下

    核心线程数由外部传参决定:核心线程数和最大线程数一致

    最大线程数由外部传参决定:核心线程数和最大线程数一致

    非核心线程数存活时间0:

    非核心线程数存活时间单位毫秒:表示创建出来的线程只要不被使用,就立马销毁

    工作队列LinkedBlockingQueue链表队列:我们进LinkedBlockingQueue的构造方法看下

    无参构造,默认是int类型的最大值21亿+

    总结:newFixedThreadPool这个方式创建出来的线程,创建时传参是几就会创建几个线程,后来的任务会被放到一个21亿+的队列中等待,没有非核心线程的概念,也就没有非核心线程的存活时间(所以源码中存活时间是0)

    缺点:如果任务一直源源不断到来,队列中将一直存储,队列占用内存,会导致内存溢出oom

    3、Executors.newSingleThreadExecutor():这种方式创建的线程池我们进源码看下

    核心线程数1:核心线程数和最大线程数固定始终是1

    最大线程数1:核心线程数和最大线程数固定始终是1

    非核心线程数存活时间0:

    非核心线程数存活时间单位毫秒:表示创建出来的线程只要不被使用,就立马销毁

    工作队列LinkedBlockingQueue链表队列:上面我们已经看过LinkedBlockingQueue源码中的构造了,即int类型最大值21亿+

    总结:newSingleThreadExecutor这个方式创建出来的线程,源码如名single,所以源码中只会创建1个线程,自始至终都是这1个线程在执行任务,后续任务到来都会被放到一个21亿+的队列中等待,没有非核心线程的概念,也就没有非核心线程的存活时间(所以源码中存活时间是0)

    缺点:如果任务一直源源不断到来,队列中将一直存储,队列占用内存,同样会导致内存溢出oom

    4、Executors.newScheduledThreadPool不甚了解,待日后研究补充

    四、谈谈对线程池使用时,具体应该使用哪种线程池和怎样设置参数的理解:

            这个问题其实本人暂时并未有答案,个人感觉丝毫使用弹性很大,举几个例子:

    1、在xxAPP中,线程池源码用的是Executors.newCachedThreadPool(),也就是如果也源源不断的任务通过线程池过来,就可以创建接近无数个线程(21亿+)同时工作,每个任务创建一个线程。这种方法从理论上分析会造成cpu100%,但实际该APP已经诞生n多年了,底层源码一直这样写,好像也没遇到什么问题。

    2、在xxAPP中,线程池源码是这样写的new ThreadPoolExecutor(4, 4, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());通过上面我们的解释,这样写线程池始终只会创建4个线程,其他任务到来将一直在一个接近无限大(21亿+)的队列中等待。这种写法理论上会造成内存溢出oom,但实际项目运行也已n年,似乎也没遇到什么这方面的问题。

    3、在xxAPP中,线程池源码是这样写的new ThreadPoolExecutor(5, 5, 1, TimeUnit.MINUTES, new LinkedBlockingQueue<>());这样写线程池和第2种类似。理论上会造成内存溢出oom,但实际项目运行也已n年,似乎也没遇到什么这方面的问题。

    总结:这已经超出本人理解范围,通过这几个项目的实际源码来看,似乎线程池的各种使用并未有太大影响(当然不能极端就写成1个线程执行任务)。

    就总结到这里吧,有问题再补充。以后要常写文章,常总结,加油自己!

    相关文章

      网友评论

          本文标题:线程池笔记

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