谈谈你对线程池的理解?

178 阅读4分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第1天,点击查看活动详情

写在前面

今天和大家聊聊线程池。掌握线程池是后端程序员的基本要求。如果有哪里理解不正确,非常希望大家指出,接下来大家一起分析学习吧。

为什么要用线程池?

降低资源消耗。通过重复利用已创建的线程降低线程创建、销毁线程造成的消耗。

提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。

提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控

线程池类参数详解

一、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时都会被关闭。

注意:

  1. 当 workQueue 使用的是无界限队列时,maximumPoolSize 参数就变的无意义了,比如 new LinkedBlockingQueue() 或者 new ArrayBlockingQueue(Integer.MAX_VALUE)。

  2. 使用 SynchronousQueue 队列时由于该队列没有容量的特性,所以不会对任务进行排队,如果线程池中没有空闲线程,会立即创建一个新线程来接收这个任务。maximumPoolSize 要设置大一点。

  3. 核心线程和最大线程数量相等时 keepAliveTime 无作用。

四种策略

1、AbortPolicy(被拒绝了抛出异常)

2、CallerRunsPolicy(使用调用者所在线程执行,就是哪里来的回哪里去)

3、DiscardOldestPolicy(尝试去竞争第一个,失败了也不抛异常)

4、DiscardPolicy(默默丢弃、不抛异常)

拒绝策略执行怎么办?

1、另外创建一个队列,当拒绝策略执行将任务放入队列。

通过定时任务去每隔一秒去查看线程池队列中是否有任务,没有则添加进去。

缺点: 任务会丢失,线程优雅关闭是指正常情况队列执行晚。会关闭,如果用这种方法创建则会丢失任务。

2、如果是特别重要的话就放入DB去持久化,给任务加个状态,通过状态来判断任务的执行情况。

3、其他情况要根据场景考虑比如又些任务过期淘汰。

总结

ThreadPoolExecutor 通过几个核心参数来定义不同类型的线程池,适用于不同的使用场景;其中在任务提交时,会依次判断corePoolSize, workQueque, 及maximumPoolSize,不同的状态不同的处理。技术领域水太深,如果不是日常使用,基本一段时间后某些知识点就忘的差不多了,因此阶段性地回顾与总结,对夯实自己的技术基础很有必要。