前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >ThreadPoolExecutor 的简单梳理

ThreadPoolExecutor 的简单梳理

作者头像
haifeiWu
发布2020-01-19 14:51:39
5450
发布2020-01-19 14:51:39
举报
文章被收录于专栏:haifeiWu与他朋友们的专栏

作 者:haifeiWu 原文链接:https://www.hchstudio.cn/article/2020/c5a/ 版权声明:非特殊声明均为本站原创作品,转载时请注明作者和原文链接。

还是楼主惯用的论述三连问,先问是什么,再问为什么,最后祭除终极大杀器 just do it ……

what ?

那么什么是线程池呢?总的来说,线程池是一种线程使用模式。线程的频繁创建于调度会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

why ?

上面说的是整个线程池的总体概念,当然 Java 中的线程池也是基于相同的理念设计的。在 Java 线程池可以提高线程复用,又可以固定最大线程使用量,防止无限制地创建线程。当程序提交一个任务需要一个线程时,会去线程池中查找是否有空闲的线程,若有,则直接使用线程池中的线程工作,若没有,会去判断当前已创建的线程数量是否超过最大线程数量,如未超过,则创建新线程,如已超过,则进行排队等待或者直接抛出异常。如下图所示

总的来说,线程池就是把资源池化,预先准备好,以避免不必要额外开销。

how ?

Java 提供了一套 Executor 框架,根据常用的场景对 ThreadPoolExecutor 类做了简单的封装,当然这样做的话难免有些束手束脚,所以大部分情况下都是根据自己的业务需求直接调用 ThreadPoolExecutor 实现自己的线程池。

这个框架中包括了 ScheduledThreadPoolExecutorThreadPoolExecutor 两个核心线程池。前者是用来定时执行任务,后者是用来执行被提交的任务。因为这两个线程池的原理是一样的,都是调用底层的 ThreadPoolExecutor,下面我们就重点看看 ThreadPoolExecutor 类是如何实现线程池的。

代码语言:javascript
复制
public ThreadPoolExecutor(int corePoolSize,//线程池的核心线程数量
                          int maximumPoolSize,//线程池的最大线程数
                          long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
                          TimeUnit unit,//时间单位
                          BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
                          ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
                          RejectedExecutionHandler handler) //拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务

通过上面代码,我们发现线程池有两个线程数的设置,一个为核心线程数,一个为最大线程数。在创建完线程池之后,默认情况下,线程池中并没有任何线程,等到有任务来才创建线程去执行任务。

当创建的线程数等于 corePoolSize 时,提交的任务会被加入到设置的阻塞队列中。当队列满了,会创建线程执行任务,直到线程池中的数量等于 maximumPoolSize。当线程数量已经等于 maximumPoolSize 时, 新提交的任务无法加入到等待队列,也无法创建非核心线程直接执行,当我们又没有为线程池设置具体的拒绝策略时,线程池就会抛出 RejectedExecutionException 异常,即线程池拒绝接受当前提交的任务。

当线程池中创建的线程数量超过设置的 corePoolSize,在某些线程处理完任务后,如果等待 keepAliveTime 时间后仍然没有新的任务分配给它,那么这个线程将会被回收。线程池回收线程时,会对所谓的“核心线程”和“非核心线程”一视同仁,直到线程池中线程的数量等于设置的 corePoolSize 参数,回收过程才会停止。从下面代码中可以看出

代码语言:javascript
复制
public void setCorePoolSize(int corePoolSize) {
    if (corePoolSize < 0 || maximumPoolSize < corePoolSize)
        throw new IllegalArgumentException();
    int delta = corePoolSize - this.corePoolSize;
    this.corePoolSize = corePoolSize;
    if (workerCountOf(ctl.get()) > corePoolSize)
        interruptIdleWorkers();
    else if (delta > 0) {
        int k = Math.min(delta, workQueue.size());
        while (k-- > 0 && addWorker(null, true)) {
            if (workQueue.isEmpty())
                break;
        }
    }
}

小结

到目前为止,楼主只是围绕 ThreadPoolExecutor 的构造方法,简单阐述了下 Java 中的线程池实现的基本逻辑,想要更深入了理解,也可以尝试在阅读完 JDK 的线程池实现源码之后造个轮子。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • what ?
  • why ?
  • how ?
  • 小结
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档