1、线程池优势:
- 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
- 提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
- 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。
2、线程池的七个参数:
- 核心线程数量——在线程池当中无论空闲多久都不会被删除的线程
- 线程池当中最大的线程数量——线程池当中最大能创建的线程数量
- 空闲时间(数值)——临时线程(线程池中出核心线程之外的线程)空闲了多久就会被淘汰的时间。
- 空闲时间(单位)——临时线程空闲了多久就会被淘汰的时间单位,要用枚举类TimeUnit类作为参数
- 阻塞队列——就是创建一个阻塞队列作为参数传入,就是当线程池当中线程数量已经达到了最大线程数量,允许多少个任务排队获取线程,其余的用参数七那个方案来处理。
- 线程工程——不是new一个线程,而是传入一个线程工厂(例如:Executors工具类中的defaultThreadFactory方法返回的就是一个线程工厂)
- 拒绝策略——当等待队列中也排满时要怎么处理这些任务。
3、拒绝策略
//代码实现
/**
* 之前用工具类进行创建,有好多参数不能自己设置
* 咱直接自己手动创建一个线程池,自己设置参数
* 参数一:核心线程数量 不能小于0
* 参数二:最大线程数 不能小于0,数值大于等于核心线程数量
* 参数三:空闲临时线程最大存活时间(数值) 不能小于0
* 参数四:空闲临时线程最大存活时间(单位) 用TimeUnit这个枚举类表示
* 参数五:任务队列,也就是一个堵塞队列 不能为null
* 参数六:创建线程的工厂 不能为null
* 参数七:任务的拒绝策略 不能为null
*/
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
3, // 核心线程数量
6, //最大线程数
60, //空闲临时线程最大存活时间(数值)
TimeUnit.SECONDS,//空闲临时线程最大存活时间(单位)
new ArrayBlockingQueue<>(3),//任务队列,也就是一个堵塞队列,也可以使用LinkedBlockingQueue这个阻塞队列
Executors.defaultThreadFactory(),//用线程池工具类Executors创建线程的工厂
new ThreadPoolExecutor.AbortPolicy()//任务的拒绝策略中其中一个,丢弃任务并抛出RejectedExecutionException
);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
threadPoolExecutor.submit(loopTread);
}
上面的代码我们设置最大线程数量为6,而阻塞队列可以排三个,说明当同时有超过9个任务需要执行,第10个线程就会执行拒绝策略,我设置的策略为丢弃任务,并抛出异常RejectedExecutionException。下面有结果就可以证明我们的猜测。
74行恰好是我们放入线程池中第10个任务,所以第74行抛出了RejectedExecutionException异常。
4、线程池的执行流程
- 当核心线程满时,再提交队伍就会在阻塞队列中排队
- 当核心线程满了,阻塞队列中也满了,才会创建临时线程
- 当核心线程满了,阻塞队列满了,临时线程也满了,会触发任务拒绝策略,也就是参数七
5、四种已实现的线程池
5.1 CachedThreadPool(缓存线程池)
- 特点:
- 核心线程数为0,最大线程数为无限大,意味着线程数量可以无限增加。
- 任务队列使用
SynchronousQueue
,这是一个没有存储空间的阻塞队列。 - 线程空闲时间超过60秒就会被销毁。
- 适用场景
- 适用于执行大量短时间的任务,但要注意,如果任务执行时间过长,会导致线程池中的线程数量增加,占用更多内存。
5.2 FixedThreadPool(固定线程池)
- 特点:
- 固定大小:线程池中的线程数量不会改变。
- 使用无界的阻塞队列来存放任务,确保任务按照提交顺序执行。
- 当需要执行一个任务时,FixedThreadPool会检查是否有空闲线程可用。
- 如果有,就将任务交给空闲线程执行。
- 如果没有空闲线程,就会将任务放入任务队列中等待执行。
- 适用场景:
- 限制并发线程数的场景,例如服务器资源有限、数据量较小或不可控的情况。
- 确保任务按照提交的顺序执行。
5.3 ScheduledThreadPool(定时调度线程池)
- 是一个用于执行定时任务的线程池。
- 它可以在一定延迟之后执行任务,或者周期性地执行任务。
- 只执行一次某个任务:在一定延迟之后执行一次指定的任务。
- 周期性执行某个任务:在一定延迟之后开始周期性地执行任务,执行周期间隔由指定的时间决定
5.4 SingleThreadExecutor(单个线程池)
- 特点
- 只有一个核心线程,提交的任务会依次执行,一个一个地排队执行。
- 线程池中的线程可以被复用,减少了线程创建和销毁的开销。
- 应用场景
- 顺序执行任务:需要按照提交顺序执行任务的场景。
- 避免线程同步问题:它避免了使用
synchronized
来保证线程同步的复杂性。 - 资源管理:线程池中的线程可以被复用,减少了频繁创建和销毁线程的开销。