线程池
文章目录
池化思想:线程池、字符串常量池、数据库连接池
线程池优点:
提高线程得利用率
提高程序得响应速度
便于统一管理线程对象
可以控制最大并发数
/**
* @author kz 单例模式 IoDh实现,静态内部类
* @date 2022/10/12
*/
public class ThreadPoolSingle {
private ThreadPoolExecutor executor;
//1.私有化构造器
private ThreadPoolSingle(){
executor=new ThreadPoolExecutor(8,20,10, TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
}
//2.静态内部类
public static class ThreadInner{
//3.完成当前对象的实例化
private static final ThreadPoolSingle poolSingle=new ThreadPoolSingle();
}
//4.提供公有方法,外键获取对象
public static ThreadPoolSingle getInstance(){
return ThreadInner.poolSingle;
}
}
一、线程池7大参数
1.核心线程数,最小线程数
2.最大线程数,线程池最多可以创建的线程数量
3.线程的空闲时间(回收线程)
4.空闲时间的单位
5.阻塞队列,如果当前任务超过核心线程数,就会放到阻塞队列中
6.线程工厂,创建线程
7.拒绝策略,如果当前线程池 数量已达上线,就会触发拒绝策略
excel导入导出
下载
发送短信也可以用多线程
失败就重试
二、线程池的运行过程
当创建线程池得时候就已经存在了核心线程?
1.当有任务进来得时候,就会先走核心线程,如果任务超过核心线程得数量
2.就会存到阻塞队列中(先进先出),如果任务过多,超出阻塞队列的最大容量
3.阻塞队列容量已满,线程池验证当前线程是否达到最大限制,如果没有就创建线程,处理新的任务,不是处理阻塞队列得任务.相当于插队
4.当阻塞队列已满并且线程池已达最大线程数得时候(阻塞队列+最大线程数(包含核心线程))新的任务会触发拒绝策略
5.当任务处理完毕得时候,在设置得回收时间内,没有用到除核心线程以外,已经被创建得线程,就会对改线程进行回收。
三、阻塞队列
任务队列是基于阻塞队列实现的,即采用生产者消费者模式,在 Java 中需要实现 BlockingQueue 接口。但 Java 已经为我们提供了 7 种阻塞队列的实现:
- ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列(数组结构可配合指针实现一个环形队列)。
- LinkedBlockingQueue: 一个由链表结构组成的有界阻塞队列,在未指明容量时,容量默认为 Integer.MAX_VALUE。
- PriorityBlockingQueue: 一个支持优先级排序的无界阻塞队列,对元素没有要求,可以实现 Comparable 接口也可以提供 Comparator 来对队列中的元素进行比较。跟时间没有任何关系,仅仅是按照优先级取任务。
- DelayQueue:类似于PriorityBlockingQueue,是二叉堆实现的无界优先级阻塞队列。要求元素都实现 Delayed 接口,通过执行时延从队列中提取任务,时间没到任务取不出来。
- SynchronousQueue: 一个不存储元素的阻塞队列,消费者线程调用 take() 方法的时候就会发生阻塞,直到有一个生产者线程生产了一个元素,消费者线程就可以拿到这个元素并返回;生产者线程调用 put() 方法的时候也会发生阻塞,直到有一个消费者线程消费了一个元素,生产者才会返回。
- LinkedBlockingDeque: 使用双向队列实现的有界双端阻塞队列。双端意味着可以像普通队列一样 FIFO(先进先出),也可以像栈一样 FILO(先进后出)。
- LinkedTransferQueue: 它是ConcurrentLinkedQueue、LinkedBlockingQueue 和 SynchronousQueue 的结合体,但是把它用在 ThreadPoolExecutor 中,和 LinkedBlockingQueue 行为一致,但是是无界的阻塞队列。
注意有界队列和无界队列的区别:如果使用有界队列,当队列饱和时并超过最大线程数时就会执行拒绝策略;而如果使用无界队列,任务队列永远都可以添加任务,所以设置 maximumPoolSize 没有任何意义
四、拒绝策略
ThreadPoolExecutor.AbortPolicy: 中止策略 当线程数量大于最大线程时 ,丢弃任务并抛出RejectedExecutionException异常
ThreadPoolExecutor.DiscardPolicy: 丢弃策略 线程数量大于最大线程时 ,丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy: 丢弃最老任务 丢弃队列最前面的任务,重新提交被拒绝的任务
ThreadPoolExecutor.CallerRunsPolicy: 优先处理策略 由调用线程(提交任务的线程)处理该任务
线程池的默认拒绝策略为AbortPolicy 中止策略
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
拒绝策略场景分析
4.1AbortPolicy 中止策略
如果是比较关键的业务,推荐使用此拒绝策略,这样在系统不能承载更大的并发量的时候,能够及时的通过异常发现。
4.2 DiscardPolicy 丢弃策略
使用此策略,可能会使我们无法发现系统的异常状态。建议是一些无关紧要的业务采用此策略。
4.3 DiscardOldestPolicy 丢弃最老任务
此拒绝策略,是一种喜新厌旧的拒绝策略。是否要采用此种拒绝策略,还得根据实际业务是否允许丢弃老任务来认真衡量
4.4 CallerRunsPolicy 优先处理策略
由调用线程处理该任务
五、线程池的五大状态
线程池的5种状态:Running、ShutDown、Stop、Tidying、Terminated
5.1 RUNNING
状态说明:在RUNNING状态下,线程池可以接收新的任务和执行已添加的任务。
线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建(比如调Executors.newFixedThreadPool()或者使用ThreadPoolExecutor进行创建),就处于RUNNING状态,并且线程池中的任务数为0!线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理
5.2 SHUTDOWN
状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务
当一个线程池调用shutdown()方法时,线程池由RUNNING -> SHUTDOWN
5.3 STOP
状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在执行的任务
调用线程池的shutdownNow()方法的时候,线程池由(RUNNING或者SHUTDOWN ) -> STOP
5.3TIDYING
状态说明:当所有的任务已终止,记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。
若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重写terminated()函数来实现。 当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 当线程池在STOP状态下,线程池中执行的任务为空时,会由STOP -> TIDYING
5.4 TERMINATED
状态说明:当钩子函数terminated()被执行完成之后,线程池彻底终止,就变成TERMINATED状态。
线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED
六、回收核心线程
//回收核心线程
executor.allowCoreThreadTimeOut(true);