ThreadPoolExecutor入门级

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/JavaMrZhang/article/details/89881285

是什么

JUC包下提供的一个线程池工具类。它帮我们解决了2个主要问题:

  1. 当需要执行大量异步任务的时候合理、有效的复用线程资源,防止多个任务创建多个线程资源。
  2. 能够有效限制和管理线程资源,比如控制初始化线程的数量,动态新增线程数等。

实现原理

本篇我们主要围绕它的核心设计思想讲解,不追踪源码。JUC包下提供了Excutors工厂类,让我们快速便捷的实例化ThreadPoolExecutor,但更建议用户通过手动实例化ThreadPoolExecutor来使用线程池。ThreadPoolExecutor构造器提供了7个初始化参数:

// 线程池的核心线程数
int corePoolSize
//线程池最大线程数
int maximumPoolSize
//超出核心线程外的线程处于空闲时能存活的时间,超过存活时间会自动被回收
long keepAliveTime
//keepAliveTime参数的时间单位
TimeUnit unit
//任务队列
BlockingQueue<Runnable> workQueue
// 创建线程的工厂
ThreadFactory threadFactory
//拒绝策略
RejectedExecutionHandler handler

现在我们带着以下2个问题逐步去理解这几个核心参数的意思以及整一个线程池的设计思想。

  1. ThreadPoolExecutor 它是如何实现线程的复用呢?
  2. 线程池的最大线程数、任务队列、拒绝策略何时会用到呢?

当用户提交任务至线程池内部会遵循这样的一个工作模型,如下图1-1:
图1-1
从图中可以看出它是一个典型的生产者、消费者模型。生产者即用户添加任务到线程池中,消费者即线程工作集执行添加进来的任务。当用户第一次提交任务,ThreadPoolExecutor发现当前线程数量小于 核心线程数会立即创建线程进行任务执行,直到超过核心线程数后会将提交的任务放入队列中,当核心线程执行完任务,会在从任务队列中取任务执行。任务队列中满时,仍然还有任务提交会判断当前线程数是否大于指定的最大线程数,如果小于则会再次创建线程来执行后面提交的任务,否则会触发任务拒绝策略。超出核心线程数外创建出来的线程在没有新任务继续提交的时候会存活keepAliveTime时间。
为了便于理解,举个例子假定我们对线程池设置如下参数

corePoolSize:3 
maximumPoolSize:8
keepAliveTime:1,
unit:分钟 
workQueue:数组阻塞队列,容量3
handler:抛出异常策略

那么此时线程池的工作模型如图1-1,下面我们分几种情况进行讨论并给出它对应的工作模型图

  1. 同一时刻用户提交了4个任务,其中3个任务分配给3个线程进行执行,其中一个任务被放入任务队列。此时线程池工作模型如图1-2:
    图1-2

  2. 同一时刻用户提交7个任务,其中3个任务分配给3个核心线程执行,3个任务放入任务队列,最后一个任务分配给 超出核心线程外的线程执行。线程池工作模型如图1-3:
    图1-3

  3. 同一时刻用户提交12任务,其中3个任务分配给3个线程执行,3个任务放入任务队列,5个任务分配给超出核心线程外的线程执行 ,最后一个任务被拒绝。线程池工作模型如图1-4
    图1-4

到这里ThreadPoolExecutor的基本原理已讲解完毕。但在我们使用线程池的时候需要注意两个点。第一,创建线程池时需要指定线程池名称,以便于后期bug排查。第二,当程序运行结束时,记得调用shutdown关闭线程池。

应用场景

对于接口中如有日志记录(写入数据到DB),可能需要耗时时间较长时,我们可以采用线程池对日志记录采用异步进行处理。

书写技术文章是一个循序渐进的过程,所以我不能保证每句话、每行代码都是对的,但至少能保证不复制、不粘贴,每篇文章都是自己对技术的认识、细心斟酌总结出来的。乔布斯说:我们在这个星球上的时间都很短,很少有机会去做几件真正伟大的事情,同时要做得好,我必须要趁我还年轻的时候完成这些事。
其实我想说的是,我是一枚程序员,我只想在有限的时间内尽可能去沉淀我这一生中所能沉淀下来的东西


清山绿水始于尘,博学多识贵于勤。

我有酒,你有故事吗?

微信公众号:「Java锦囊」。

欢迎一起谈天说地,聊Java。


猜你喜欢

转载自blog.csdn.net/JavaMrZhang/article/details/89881285