一、常见线程池介绍
上一节中线程池使用到了Executors.newSingleThreadExecutor()方法。接下来介绍下常用的线程池。
newSingleThreadExecutor- newSingleThreadExecutor():创建一个单线程化的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
- newFixedThreadPool(int nThreads):创建一个可重用固定个数的线程池,每提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
-
newCachedThreadPool(): 推荐使用。可缓存线程池,先查看池中有没有以前建立的线程,如果有,就直接使用。如果没有,就建一个新的线程加入池中,缓存型池子通常用于执行一些生存期很短的异步型任务
-
newScheduledThreadPool(int corePoolSize):创建一个定长线程池,支持定时及周期性任务执行。
在性能上newCachedThreadPool>newFixedThreadPool>newSingleThreadExecutor。通过截图可以看到这三个方法都调用了ThreadPoolExecutor的构造方法,在执行Executors.线程池方法时,idea会提示
给我们的解释如下:
线程池不允许使用Executors去创建,而是通过ThreadPoolExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。 说明:Executors返回的线程池对象的弊端如下:
1)FixedThreadPool和SingleThreadPool: 允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM。
2)CachedThreadPool: 允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。
因此在使用线程池时,我们也要放弃前面使用Executors类创建线程池,采用ThreadPoolExecutor类来创建线程池。
二、ThreadPoolExecutor构造函数参数介绍
ThreadPoolExecutor构造器共有7个参数:
-
corePoolSize: 核心线程数
-
maximumPoolSize:池中允许的最大线程数
-
keepAliveTime: 超时时间,当线程数大于核心数时,当空闲时间达到keepAliveTime时,线程会被销毁,直到只剩下corePoolSize个线程
-
TimeUnit: 超时时间的单位,秒,毫秒,微秒,纳秒等,与keepAliveTime参数共同决定超时时间。
-
workQueue:表示如果任务数量超过核心池大小,多余的任务添加到阻塞队列中
-
threadFactory:线程工厂
-
handler:线程池对拒绝任务的处理策略
下图简单介绍了核心线程数、最大线程数工作,分析下核心线程数和最大线程数的关系,有助于加深理解:
(1)当提交一个任务时,先判断线程池中当前线程数量是否达到了corePoolSize,若未达到,则新建线程运行此任务,线程池创建一个新线程(worker)执行任务(task)且任务结束后将该线程保留在线程池中,不做销毁处理。除非设置了allowCoreThreadTimeOut=true时,无论线程数多少,那么线程处于空闲状态超过一定时间(keepAliveTime)就会被销毁掉。
(2)如果达到了corePoolSize,判断工作队列(workQueue)是否已满,未满则将新的任务提交到工作队列中
(3)如果workQueue满了,判断线程池中的线程数量是否达到了maximumPoolSize,如果未达到,则新建一个工作线程来执行这个任务,如果达到了则使用饱和策略来处理这个任务。注意: 在线程池中的线程数量超过corePoolSize时,对于右侧的非核心线程数每当有线程的空闲时间超过了keepAliveTime,这个线程就会被终止。直到线程池中线程的数量不大于corePoolSize为止。
线程池工作workQueue
用来保存等待被执行的任务的阻塞队列,且任务必须实现Runnable接口,阻塞队列如下:
- ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务;
- LinkedBlockingQuene:基于链表结构的无界阻塞队列,按FIFO排序任务,吞吐量通常要高于ArrayBlockingQuene;
- PriorityBlockingQueue:使用平衡二叉树堆,实现的具有优先级的无界阻塞队列
- DelayQueue:无界阻塞延迟队列,队列中每个元素均有过期时间,当从队列获取元素时,只有过期元素才会出队列。队列头元素是最块要过期的元素。
- SynchronousQuene:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量通常要高于LinkedBlockingQuene;
饱和策略
当工作队列满且线程个数达到maximunPoolSize后所采取的策略
- AbortPolicy:默认策略;新任务提交时直接抛出未检查的异常RejectedExecutionException,该异常可由调用者捕获。
- CallerRunsPolicy:既不抛弃任务也不抛出异常,使用调用者所在线程运行新的任务。
- DiscardPolicy:丢弃新的任务,且不抛出异常。
-
DiscardOldestPolicy:调用poll方法丢弃工作队列队头的任务,然后尝试提交新任务
自定义策略:根据用户需要定制。
参考:
https://zhuanlan.zhihu.com/p/87733949
https://blog.csdn.net/qq_33323054/article/details/106923732
网友评论