线程池笔记

2022-06-11  本文已影响0人  zmy26

前言:线程池相关的所有类都在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个线程执行任务)。

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

上一篇下一篇

猜你喜欢

热点阅读