一、线程池是什么
一般来说,线程池是一种池化技术的实现,通过一个或者多个线程来执行用户提交的任务,解决了每执行一个任务就创建一个线程造成的资源浪费问题,同时也提供了很多对线程进行管理的操作方法,比如停止一个线程或者统计已经完成的任务量等等。
-
类结构
image.png
从类的结构图中看到,ThreadPoolExecutor实现了Executor以及ExecutorService的接口
1、Executor:Executor提供了execute()接口,ThreadPoolExecutor对其进行了实现
2、ExecutorService:ExecutorService定义了shutdown()、submit()等接口,主要作用是对线程池状态的控制以及定义任务提交的方式
3、AbstractExecutorService:AbstractExecuorService是模板方法设计模式的实现,提供了ExecutorService接口的默认实现,提供线程池通用的逻辑能力,比如submit()方法
二、类参数
-
阻塞队列
-
ArrayBlockingQueue
image.png -
LinkedBlockingQueue
image.png -
DelayQueue
image.png -
SynchronousQueue
可以简单的理解为:如果要往队列放元素必须要有另一个线程想从队列中取元素,没有现场要取你就放不进去
image.png -
TransferQueue
TransferQueue可以理解为SynchronousQueue的加强版,比如公平锁,按请求先后顺序进行处理
image.png -
PriorityBlockingQueue
image.png
-
拒绝策略
- DiscardOldestPolicy 丢弃队列中第一个任务
- AbortPolicy 抛异常
- CallerRunsPolicy 交给提交过来的线程去执行
- DiscardPolicy 丢弃,不做任何操作
三、线程池类型
newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理所需,可灵活回收空闲线程,若线程数不够,则新建线程。
newFixedThreadPool:创建一个固定大小的线程池。可控制并发的线程数量,如果工作线程数量达到线程池初始的最大数,则将提交的任务存入到池队列中。
newSingleThreadExecutor:创建一个单线程的线程池,即只创建唯一的工作者线程来执行任务,,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
-
newScheduleThreadPool:创建一个定长的线程池,支持定时及周期性任务执行。
image.png
四、踩坑经历
- 工作中,很多小伙伴使用newCachedThreadPool新建线程池,这个线程池的最大线程数是Integer.Max值,正常情况下,是会导致线程池线程太多,耗尽服务器资源导致性能问题,所以使用时考虑清楚
- newFixedThreadPool,任务队列大小可达Integer.Max,正常情况任务堆积过多绝对导致OOM,已经目测过两次
- 如果线程池中的任务队列大小在业务发展过程中不会很大,可以使用线程池,不过一般我建议使用MQ,原因有两个:一是如果发生重启或者服务器宕机,队列中的任务就会丢失,二是很难控制队列中的任务不会过大,而MQ会自动帮我们保存消息