浅析java线程池
Java线程池
1. 常见的四大线程池
- Executors.newSingleThreadExecutor();
- Executors.newFixedThreadPool();
- Executors.newScheduledThreadPool();
- Executors.newCachedThreadPool();
2. ThreadPoolExecutor的七大参数
- Int corePoolSize
- 核心线程数
- int maximumPoolSize
- 最大线程数(线程池中,最多存在多少个线程)
- long keepAliveTime
- 保持活跃的时间(在指定时间内,如果没有任务,释放非核心线程)
- TimeUnit unit
- 时间单位(参数TimeUnit中的常量)
- BlockingQueue<Runnable> workQueue
- 等待队列
- ThreadFactory threadFactory
- 创建线程的工厂
- RejectedExectuionHandler handler
- 拒绝策略
3. java四大线池程本质
- java线程池本质上只有一个。
- 无论哪个线程池,都是调用ThreadPoolExecutor创建出来的。
- 线程池有不同的特性,是因为创建线程池的时候,参数不一样。
3.1 Executors.newSingleThreadExecutor简单线程池
调用ThreadPoolExecutor构造函数时的七个参数值。
参数含义 | 参数名称 | 参数类型 | 取值 | 备注 |
---|---|---|---|---|
核心线程 | corePoolSize | int | 1 | 核心线程数为有且只有一个 |
最大线程 | maxNumPoolSize | int | 1 | 最大线程数量只能存在一个 |
存活时间 | keepAliveTime | long | 0L | 线程保持活跃时间长度,加上时间单位计时 |
时间单位 | unit | TimeUnit | TimeUnit.MILLISECONDS | 参数TimeUnit中的常量 |
等待队列 | workQueue | BlockingQueue<Runnable> | LinkedBlockingQueue<Runnable> | 等待队列长度为Integer.MAX_VALUE(int最大值) |
有且只有一个线程工作,队列无穷大,所有的任务,都交个这个线程来处理,不论任务多少,使用线程池中的唯一线程,依次的执行队列中的任务。
当核心线程数和最大线程数相等的时候,保持活跃的时间设置时没有意义的,应为核心线程不会被释放,最大线程数不会超过核心线程数,所以当任务执行完成之后,不会进行线程释放
等待队列 new LinkedBlockingQueue<Runnable>() 无界队列,构造方法中的初始长度为Integer.MAX_VALUE(int最大值)
等待队列无穷大,意味着该线程池时没有拒绝策略
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
3.2 Executors.newFixedThreadPool固定线程池
调用ThreadPoolExecutor构造函数时的七个参数值,在创建这个线程池的时候,需要指定一个参数,该参数指定的是核心线程数和最大线程数
Executors.newFixedThreadPool(int size)
参数含义 | 参数名称 | 参数类型 | 取值 | 备注 |
---|---|---|---|---|
核心线程 | corePoolSize | int | size | size由调用方法时,指定的数量决定 |
最大线程 | maxNumPoolSize | int | size | size由调用方法时,指定的数量决定 |
存活时间 | keepAliveTime | long | 0L | 线程保持活跃时间长度,加上时间单位计时 |
时间单位 | unit | TimeUnit | TimeUnit.MILLISECONDS | 参数TimeUnit中的常量 |
等待队列 | workQueue | BlockingQueue<Runnable> | LinkedBlockingQueue<Runnable> | 等待队列长度为Integer.MAX_VALUE(int最大值) |
和newSingleThreadExecutor的区别在于,创建线程池时,需要指定一个数量size,size在其内部调用ThreadPoolExecutor构造方法时,作为核心线程数和最大线程数[同样导致活跃时间无效],等待队列也是无穷大
Executors.newFixedThreadPool和newSingleThreadExecutor的唯一区别就是需要指定一个线程数量
3.3 Executors.newScheduledThreadPool 安排
调用ThreadPoolExecutor构造函数时的七个参数值,在创建这个线程池的时候,需要指定一个参数,该参数指定的是核心线程数
Executors.newScheduledThreadPool(int corePoolSize)
参数含义 | 参数名称 | 参数类型 | 取值 | 备注 |
---|---|---|---|---|
核心线程 | corePoolSize | int | size | size由调用方法时,指定的数量决定 |
最大线程 | maxNumPoolSize | int | Integer.MAX_VALUE | 等于无穷大 |
存活时间 | keepAliveTime | long | 0L | 线程保持活跃时间长度,加上时间单位计时 |
时间单位 | unit | TimeUnit | NANOSECONDS | 参数TimeUnit中的常量 |
等待队列 | workQueue | BlockingQueue<Runnable> | new DelayedWorkQueue() | 该队列的作用:队列中有等待任务,直接创建新线程 |
当前线程池,只指定一个初始化的核心线程数,等待队列相当于没有缓存机制,进入队列的任务会立刻创建新的线程来执行任务,由于线程保持存活的时间为0所以,超过核心线程数的线程在执行完任务后,会被释放
当达到核心线程数满了以后,就会创建新的线程,当新开出的线程,在完成任务之后,就会被直接销毁。当前线程池在理论上可以创建无穷个线程
DelayedWorkQueue
队列初始化的时候,长度为0,队列不缓存任务,入队列即刻出队列
3.4 Executors.newCachedThreadPool 缓存
调用ThreadPoolExecutor构造函数时的七个参数值
Executors.newCachedThreadPool()
参数含义 | 参数名称 | 参数类型 | 取值 | 备注 |
---|---|---|---|---|
核心线程 | corePoolSize | int | 0 | 核心线程为0 |
最大线程 | maxNumPoolSize | int | Integer.MAX_VALUE | 等于无穷大 |
存活时间 | keepAliveTime | long | 60L [一分钟] | 线程保持活跃时间长度,加上时间单位计时 |
时间单位 | unit | TimeUnit | TimeUnit.SECONDS | 参数TimeUnit中的常量 |
等待队列 | workQueue | BlockingQueue<Runnable> | SynchronousQueue<Runnable> | 该队列的作用:队列中有等待任务,直接创建新线程 |
当前线程池,没有核心线程数量,有任务的时候,启动线程,在线程任务完成之后,等待60秒,在此期间如果有新的任务,复用线程,如果没有新的任务,超过线程存活时间60秒,则销毁线程
4. 线程池的作用
创建线程消耗的性能消耗比较大,将创建的线程存放起来,在线程执行后,不释放。线程的复用性提高,多任务的时候,其中一个线程执行完毕后,可以直接接受下一个任务,避免了创建线程的性能和时间消耗。
不用每次执行任务的时候,都需要创建线程,线程可以复用的情况下,直接从线程池中获取线程,在线程完成任务后,不被释放,可以接受下一次任务。
举例,一个银行平时只开三个窗口(核心线程数),某天业务量爆发,需要办理业务的人增多。三个窗口每个窗口每次只能帮一个人办理业务,很多人都只能在大厅等待(等待队列),如果大厅坐满了等待的人,银行会觉得需要办理业务的人太多了,于是会多开几个窗口,但不能大于银行本身的窗口最大数,当银行开启了足够多的窗口之后,大厅的人数开始减少。
当大厅左右人都办理完业务离开两分钟之后(保持的活跃时间,时间单位),后开的那些窗口的任务完成了,就会将窗口关闭。
另一种情况,线程池中的所有线程全部处在执行任务的状态。银行所有的窗口,都在处理业务,大厅的人没有减少,反而还在增加,这种情况下,银行需要告知后面来的人,需要换个时间再来办理,在线程池中是拒绝策略。
5. 自定义线程池
使用ThreadPoolExecutor自定义线程池。
线程池在刚创建出来的时候,里面是没有线程的。
在我们向线程池提交任务的时候,线程池才会开始创建线程。
如果核心线程数是10,最大的线程数是20,当我们提交第11个的时候(前十个没有结束),线程池会创建新的线程吗?
线程池创建线程的策略,先把核心线程数填满,然后填满等待队列,等待队列如果满了,就创建新的线程,但是总线程数不能大于最大线程数。
6. 线程池常见问题
- 线程池刚创建的的时候,内部存在几个线程?
- 线程池在刚创建的时候,内部没有线程
- 线程池在创建之后,什么时候才会产生线程?
- 在程序运行过程中,向线程池提交任务的时候,线程池才会创建线程
- 如果核心线程数是10,最大线程数是20,当提交第11个任务是(前10个线程执行未结束),线程池会不会创建新的线程?
- 不会,线程池创建线程的策略,先把核心线程数填满,然后填满等待队列,等待队列如果满了,就创建新的线程,总线程数不能大于最大线程数
核心线程永不销毁
拒绝策略后续继续补充…
做一个记录