线程池基础笔记
学习视频地址:https://www.bilibili.com/video/BV1wh411e7nd?share_source=copy_web
一、线程池基础
1. 什么是线程池?为什么要使用线程池?
1、什么是线程池
- 线程池是一种基于池化思想管理线程的工具
2、为什么要使用线程池
- 一个线程对应一个任务
- 如果要执行多个任务,则需创建多个线程
- 传统的方法,线程不能复用,一次只执行一个任务就被销毁了
3、线程池如何执行任务
4、线程池的好处
- 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度:当有任务时,任务可以不需要等到线程创建就能立即执行
- 提高线程的可管理性:线程池可以进行统一的分配,调优和监控
2. 如何使用原生方式创建线程池
阿里巴巴开发手册
-
ThreadPoolExecutor(int ,int ,long ,TimeUnit ,BlockingQueue)
-
ThreadPoolExecutor(int ,int ,long ,TimeUnit ,BlockingQueue ,ThreadFactory)
-
ThreadPoolExecutor(int ,int ,long ,TimeUnit ,BlockingQueue ,RejectedExecutionHandler)
-
ThreadPoolExecutor(int ,int ,long ,TimeUnit ,BlockingQueue ,ThreadFactony , RejectedExecutionHandler)
-
必须参数:
- 核心线程数
- 最大线程数
- 空闲非核心线程存活时间
- 时间单位
- 线程等待队列容器
-
可选参数
- (自定义线程工厂)
- (线程拒绝策略)
![image-20220707103826204](https://i-blog.csdnimg.cn/blog_migrate/65bd71108b002e91ed38e60b51e50c5e.png)
线程池形象化
等待任务队列形象化
![image-20220707104213985](https://i-blog.csdnimg.cn/blog_migrate/acaac47540f3ac9105bf5a0c91633d12.png)
- 任务队列一般用
- ArrayBlockingQueue
- LinkedBlockingQueue
线程工厂
![image-20220707104339665](https://i-blog.csdnimg.cn/blog_migrate/04a76192c2f84201be81139be98a1c38.png)
![image-20220707104350344](https://i-blog.csdnimg.cn/blog_migrate/ce096b96b6616b41ed2ff22ccf8bbc32.png)
![image-20220707104401295](https://i-blog.csdnimg.cn/blog_migrate/3f16c8c8bc31fe3a93c096a41c70be06.png)
任务拒绝策略(后续详解)
3. 有风险,需慎用的创建方法
- FixedThreadPool:固定大小的线程池
- SingleThreadExecutor:单个线程的线程池
- CachedThreadPool:可缓存的线程池
创建线程池的方式
风险
- FixedThreadPool:固定大小的线程池
-
-
- 默认使用的是 LinkedBlockingQueue,然后其容量默认为 Integer 的最大值
- 有资源耗尽,内存被爆掉的风险
-
- SingleThreadExecutor:单个线程的线程池
- 默认使用的是 LinkedBlockingQueue,风险和 FixedThreadPool 一样
- CachedThreadPool:可缓存的线程池
总结
![image-20220707105118759](https://i-blog.csdnimg.cn/blog_migrate/bcf3816a524c96989341d4605c1286f7.png)
4. 线程提交方式:excute 和 submit 的区别
- excute 只适合提交 Runnable 无返回值的任务
- submit 适用范围较广
![image-20220707105244809](https://i-blog.csdnimg.cn/blog_migrate/64ba3ebc985076d4d9283194159fc540.png)
![image-20220707105320512](https://i-blog.csdnimg.cn/blog_migrate/5330e8abf6c2c0802a99d8ef60a1fd70.png)
总结区别
二、获取任务执行结果:Future 类
1. Future获取任务执行结果(阻塞式和定时式)
Future对象
- 是一个接口
-
阻塞式
![image-20220707110118863](https://i-blog.csdnimg.cn/blog_migrate/691cdf5d318f8d6b468f0cb1101bf7e7.png)
![image-20220707110037395](https://i-blog.csdnimg.cn/blog_migrate/5d367e07ece2f3e479c8529749a33a42.png)
定时式
超时为得到执行结果则抛出异常,捕获该异常可执行其他任务。
![image-20220707110249415](https://i-blog.csdnimg.cn/blog_migrate/985bdb0d69c15e728379e59407dcd639.png)
2. Futrue取消任务:Cancle方法
![image-20220707110531244](https://i-blog.csdnimg.cn/blog_migrate/daa7b930987c13291996dfb2848186dc.png)
Cancle 方法的 3 种情况
- 取消未执行的任务
- 取消已完成的任务
- 取消正在执行的任务
取消正在执行中的线程
-
参数为 false:
- 抛出取消成功的异常
- 但任务还是会继续执行完
- 但 get 是获取不到东西了的,会抛异常
-
参数为 true:
-
抛出取消成功的异常
-
但任务并非立马结束的
-
需要响应中断线程的指令
-
即,如果任务中有死循环,则无法响应中断指令
-
这样响应线程中断指令即可成功地真正意义上的取消
-
// 当线程没被中断时 while (!Thread.interrupted()) { // 递增i i++; }
-
-
三、任务拒绝策略
![image-20220707111459264](https://i-blog.csdnimg.cn/blog_migrate/a08b1311228211cd0d92385c4422be3a.png)
这四个类,都实现了同一个接口
使用
创建线程池时,传入该参数即可
四、关闭线程池
1. 关闭线程池:shutDown
该方法执行后:
- 线程池及任务队列不允许添加新任务
- 会继续执行完线程池及任务队列中还未执行完的任务
2. 关闭线程池:shutDownNow
该方法执行后:
- 线程池不允许添加新任务
- 会给线程池中的任务发送中断指令
- 能响应中断指令的任务将会被中断
- 线程池关闭后,返回任务队列中的任务:即等待中未执行的任务
- 返回任务队列中的任务给父线程,父线程可以选择继续执行或不操作不接收等等
与 shutdown 的区别
五、线程池深入探究
1. 线程池状态及生命周期
状态
![image-20220707112929493](https://i-blog.csdnimg.cn/blog_migrate/ecd427421d89504c8ded19494421e6b7.png)
生命周期
2. 线程池是怎样执行任务的
![image-20220707130819737](https://i-blog.csdnimg.cn/blog_migrate/f949cbb12d80117247b2948c08fe2cc7.png)
excute方法详情
![image-20220707131058614](https://i-blog.csdnimg.cn/blog_migrate/52589d7fa0a6749b859cb5eedb5a6438.png)
- addWorker( 任务内容 , 是否做核心线程)
- addWorker(null, false):添加一条非核心线程。
- 添加后会自动去接收任务队列中的任务执行。
- ctl.get( ) :获取线程池状态、线程数
- isRunning( c ):判断线程池是否在运行,即线程池是否被关闭或者停止。
- workerQueue.offer( command ):添加任务到任务队列中。
- remove( command ):移除任务。
- reject( command ):拒绝任务。
六、线程池处理任务的多种情况
1. 如何批量执行任务
可以看出,能批量执行的,只能是 Callable 任务!
- invokeAll
- 是按集合中任务顺序执行任务的
- 也是按顺序返回执行结果的
- 超时的invokeAll & invokeAny
- 时间到了之后
- 未完成的任务直接全部取消
- 已完成的任务,结果全部返回给调用者
- invokeAny
- 执行批量任务,返回最先完成的任务的执行结果
- 然后取消未完成的任务
2. 如何执行定时、延时任务?
ScheduledThreadPoolExecuter
- 调度线程池
3. 如何执行周期、重复性任务?
-
scheduleAtFixedRate
-
scheduleWithFixedDelay
-
方法内的参数
- 任务
- 执行前延时时间
- 固定时间 / 间隔时间
- 时间单位
-
区别
七、线程池扩展
1. Forkjoin框架
ForkJoin 是一个把大任务分割成若干个小任务,再对每个小任务得到的结果进行汇总,得到大任务结果的框架。
2. 使用 ForkJoinPool
![image-20220707163947055](https://i-blog.csdnimg.cn/blog_migrate/6c80e05a1feab8f75759a979863cb636.png)
使用例子
任务:从 1 加到 100
3. 如何使用 ExecutorCompletionService
- 返回结果采用:执行优先原则,即先执行完的先返回
4. 如何监控线程池
线程池的监控点
- 线程的变化情况
- 任务的变化情况