述
在日常开发中,当我们需要使用多线程来处理一些任务的时候,可以创建一个线程,然后去调用 start()
启动,这种方式比较简单,但是同时也带来一些问题,比如在任务数量多的情况下,频繁的创建销毁线程,就会给服务器带来较大的压力,处理效率也不是特别高, 所以在这种情况下,我们就可以使用线程池来处理这些任务,创建固定数量的线程去处理任务,避免一直创建和销毁线程带来的开销. 下面就对线程池做一些详细的了解.
线程池的作用
线程池的作用就是创建一定数量的线程,这些线程都保持工作的状态,有任务进来就交给这些线程,这些线程都可以去反复执行任务,从而避免反复创建线程的损耗.
使用线程池可以加快响应速度,合理利用CPU和内存,且可以统一管理
线程池的适用场景
当服务器收到大量的请求时,使用线程池技术就比较合适,可以减少线程的创建和销毁次数,提高服务器的工作效率,在日常开发中,如果需要创建5个以上的线程,就可以使用线程池来管理
创建和停止线程池
上面说了这么多线程池的好处,下面来看一下如何创建线程池, 首先要看一下线程池的构造函数
线程池的构造函数
进入 ThreadPoolExecutor
这个类看一下它的构造函数, 如图
这里可以看到一共有以下几个参数,分别看一下具体的意思
参数名 | 作用 |
---|---|
corePoolSize | 核心线程数 |
maximumPoolSize | 最大线程数 |
keepAliveTime | 保持存活时间 |
unit | 时间单位 |
workQueue | 任务存储队列 |
threadFactory | 创建线程工厂 |
handler | 线程池无法接受新的任务的时候的拒绝策略 |
那么这些参数具体的作用是什么? 首先是 corePoolSize
和 maximumPoolSize
corePoolSize
: 核心线程数,线程池在初始化完成之后,默认情况下线程池中是没有线程的,线程池会等有任务到来的时候去创建新线程执行maximumPoolSize
: 在线程池运行过程中,可能会在核心线程数的基础上再创建一些线程,但是总的线程数有一个上线,就是maximumPoolSize
keepAliveTime
: 就是在核心线程以外的线程的回收时间,当核心线程数意外的线程空闲超过keepAliveTime
这个时间的时候就会被回收workQueue
: 存放任务的队列,当核心线程都有任务在执行的时候,新来的任务就会被放到任务队列中等待执行threadFactory
: 创建线程的工厂,如果不设置的话,默认是DefaultThreadFactory
,通常情况下也不会去自定义handler
: 线程池处理任务达到极限的时候就不能再接受新的任务了,这个参数就是设置拒绝的策略
创建线程的规则
了解了构造函数中参数的作用之后,再来了解一下线程池创建线程的规则
当有新的任务进来的时候,会有以下判断:
- 判断线程池当前线程数是否小于
corePoolSize
, 如果当前线程数量小于核心线程数量,就会新建一个线程来运行 - 如果线程数大于等于
corePoolSize
但小于maximumPoolSize
,就会将任务放到队列中去 - 如果队列满了,并且线程数小于
maximumPoolSize
就会创建新的线程来执行 - 如果队列满了,并且线程数大于等于
maximumPoolSize
就拒绝该任务
画个图来看一下这个流程
所以判断是否新创建线程的顺序是 corePoolSize
-> workQueue
-> maximumPoolSize
举个例子,现在有一个 corePoolSize=5
, maximumPoolSize=10
, workQueue=100
的线程池,有一批任务提交给这个线程池, 然后前五个任务都会创建新的线程去执行,然后后面的任务都会先存到队列里面,直到队列里面放了100个任务,队列满了之后,再进来的任务将会创建新的线程执行,最多创建10个,之后再有任务进来,就会拒绝,所以这个线程池最多能同时处理 115 个任务
增减线程的特点
通过上面这个创建线程的规则,可以看出以下几个线程池的特点
- 创建线程池的时候
corePoolSize
和maximumPoolSize
设置为相同的时候,就是一个固定线程数量的线程池 - 线程池希望保持较小的线程数,只有在任务特别多,负载高的情况下再创建额外的线程去处理
maximumPoolSize
可以设置为很高的值,比如Integer.MAX_VALUE
, 就表示线程池可以容纳任意数量的任务- 只有当
workQueue
满了的时候才会创建额外的线程去处理,所以如果使用的是无界队列,线程池的线程数量就不会超过corePoolSize
常见的队列类型有以下三种:
- 直接交接:
SynchronousQueue
- 无界队列:
LinkedBlockingQueue
- 有界队列:
ArrayBlockingQueue
这里的直接交接就是这个队列不存放任务,直接交给线程池去执行
总结
- 了解线程池的作用,以及线程池解决了哪些问题
- 创建线程池的几个关键参数
- 线程池创建线程的规则