写在前面
今天和大家聊聊线程池。掌握线程池是后端程序员的基本要求。如果有哪里理解不正确,非常希望大家指出,接下来大家一起分析学习吧。
为什么要用线程池?
降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。
提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
线程池类参数详解
一、corePoolSize
核心线程数量,线程池维护线程的最少数量
二、maximumPoolSize
线程池维护线程的最大数量
三、keepAliveTime
线程池除核心线程外的其他线程的最长空闲时间,超过该时间的空闲线程会被销毁
四、unit
keepAliveTime的单位,TimeUnit中的几个静态属性:NANOSECONDS、MICROSECONDS、MILLISECONDS、SECONDS
五、workQueue
线程池所使用的任务等待队列
六、threadFactory
线程工厂,用于创建线程,一般用默认的即可
七、handler
线程池对拒绝任务的处理策略
线程池的工作流程
一、submit ⼀个任务,判断当前的活跃线程数⼤⼩是否⼤于 corePoolSize,如果⼩于则使⽤threadFactory 新建⼀个线程来执⾏当前任务。
二、如果线程池当前的活跃线程数⼤于 corePoolSize,再判断当前 workQueue 是否已经满了,如果workQueue 没有满,则将任务加⼊的 workQueue 等待被执⾏。
三、如果 workQueue 已经满了,并且 maximumPoolSize ⼤于 corePoolSize,并且当前活跃线程数⼩于 maximumPoolSize,这个时候会新建⼀个线程执⾏任务。
四、如果当前活跃线程数⼤于 maximumPoolSize,则执⾏任务拒绝策略。
五、如果线程池中的线程数超过 corePoolsize,那么超过核⼼线程数的线程在空闲时间超过 keepAliveTime,会被关闭回收。
六、如果允许线程池核⼼线程超时,那么所有线程在空闲时间超过keepAliveTime时都会被关闭。
注意:
- 当 workQueue 使用的是无界限队列时,maximumPoolSize 参数就变的无意义了,比如 new LinkedBlockingQueue() 或者 new ArrayBlockingQueue(Integer.MAX_VALUE)。
- 使用 SynchronousQueue 队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize 要设置大一点。
- 核心线程和最大线程数量相等时 keepAliveTime 无作用。
四种策略
1、AbortPolicy(被拒绝了抛出异常)
2、CallerRunsPolicy(使用调用者所在线程执行,就是哪里来的回哪里去)
3、DiscardOldestPolicy(尝试去竞争第一个,失败了也不抛异常)
4、DiscardPolicy(默默丢弃、不抛异常)
拒绝策略执行怎么办?
1、另外创建一个队列,当拒绝策略执行将任务放入队列。
通过定时任务去每隔一秒去查看线程池队列中是否有任务,没有则添加进去。
缺点: 任务会丢失,线程优雅关闭是指正常情况队列执行晚。会关闭,如果用这种方法创建则会丢失任务。
2、如果是特别重要的话就放入DB去持久化,给任务加个状态,通过状态来判断任务的执行情况。
3、其他情况要根据场景考虑比如又些任务过期淘汰。
总结
ThreadPoolExecutor 通过几个核心参数来定义不同类型的线程池,适用于不同的使用场景;其中在任务提交时,会依次判断corePoolSize, workQueque, 及maximumPoolSize,不同的状态不同的处理。技术领域水太深,如果不是日常使用,基本一段时间后某些知识点就忘的差不多了,因此阶段性地回顾与总结,对夯实自己的技术基础很有必要。