浅析java线程池
---- 此文来自某位渣男 他说:核心线程永不销毁 https://blog.csdn.net/weixin_35722483/article/details/123367288?spm=1001.2014.3001.5502
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个线程执行未结束),线程池会不会创建新的线程?
不会,线程池创建线程的策略,先把核心线程数填满,然后填满等待队列,等待队列如果满了,就创建新的线程,总线程数不能大于最大线程数