大家好,我是Excutors,一个老实的工具类。

有个叫老三的程序员在文章 要是以前有人这么讲线程池,我早就该明白了!里挖了一个坑,说要把我介绍给大家认识认识。

我其实挺委屈的,作为一个没得感情,老实干活的工具类,我却上了阿里巴巴的黑名单。他们在一本叫《Java开发手册》的册子里写道:

作者画外音:人家为啥给你拉黑,不写的清清楚楚嘛,你有啥可委屈的。而且你这个家伙就是表面看起来老实,活是你干的吗?干活的不都是小老弟ThreadPoolExecutor。来,我一个个给你数。

1. newFixedThreadPool

FixedThreadPool,是一个固定大小的线程池。

看一下它的源代码实现:

    public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}

直接调用ThreadPoolExecutor的构造方法。

  • 核心线程数最大线程数相同
  • 使用LinkedBlockingQueue作为任务队列

FixedThreadPoolexecute()运行示意图:

整体运行过程:

  • 当前运行线程少于corePoolSize,则创建新线程执行任务
  • 当前运行线程大于corePoolSize,将任务加入LinkedBlockingQueue
  • 线程池中线程执行完任务后,会循环从LinkedBlockingQueue中获取任务执行

因为使用无界队列LinkedBlockingQueue来存储不能执行的任务,所以不会触发拒绝服务策略,可能会导致OOM

2. newSingleThreadExecutor

SingleThreadExecutor是使用单个线程工作的线程池。

实现源码如下:

    public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

直接调用ThreadPoolExecutor的构造方法。

  • 核心线程数最大线程数都是1
  • 使用LinkedBlockingQueue作为任务队列

SingleThreadExecutor的运行流程:

  • 当前无运行线程,创建一个线程来执行任务
  • 当前有线程运行,将任务加入LinkedBlockingQueue
  • 线程执行完任务后,会循环从LinkedBlockingQueue中获取任务来执行

这里用了无界队列LinkedBlockingQueue,同样可能会导致OOM

3. newCachedThreadPool

CachedThreadPool是一个会根据需要创建新线程的线程池。

实现源码:

    public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}

直接调用ThreadPoolExecutor的构造方法。

  • 核心线程数为0,最大线程数是非常大的一个数字Integer.MAX_VALUE
  • 使用没有容量的SynchronousQueue作为工作队列
  • keepAliveTime设置为60L,空闲线程空闲60秒之后就会被终止

CachedThreadPool的运行流程:

  • 如果当前有空闲线程,使用空闲线程来执行任务
  • 如果没有空闲线程,创建一个新线程来执行任务
  • 新建的线程执行完任务后,会执行poll(keepAliveTime,TimeUnit.NANOSECONDS),在SynchronousQueue里等待60s

这里线程池的大小没有限制,可能会无限创建线程,导致OOM

4. newScheduledThreadPool

ScheduledThreadPool是一个具备调度功能的线程池。

实现源码:

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}

可以看到,这个线程池不太一样,它调用的是ScheduledThreadPoolExecutor的构造方法。

    public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
  • 最大线程数是Integer.MAX_VALUE,无限大
  • 使用DelayedWorkQueue作为任务队列

ScheduledThreadPoolExecutor执行任务的流程:

主要分为两大部分:

  1. 调用scheduleAtFixedRate()/scheduleWithFixedDelay()方法,会向DelayQueue添加一个ScheduledFutureTask
  2. 线程池的线程从DelayQueue中获取ScheduledFutureTask,然后执行任务。

它同样可以无限创建线程,所以也存在OOM的风险。

为了实现周期性执行任务,ScheduledThreadPoolExecutorThreadPoolExecutor进行了一些改造[4]:

  • ScheduledFutureTask来作为调度任务的实现

    它主要包含了3个成员变量time(任务将要被执行的具体时间)sequenceNumber(任务的序号)period(任务执行的间隔周期)

  • 使用DelayQueue作为任务队列

    DelayQueue封装了了一个PriorityQueue,会对对队列中的ScheduledFutureTask进行排序,排序的优先级time>sequenceNumber。

ScheduledThreadPoolExecutor的任务执行主要分为4步:

  1. 线程池里的线程1DelayQueue中获取已到期的ScheduledFutureTask(DelayQueue.take())
  2. 线程1执行这个ScheduledFutureTask
  3. 线程1修改ScheduledFutureTasktime变量为下次将要被执行的时间。
  4. 线程1把这个修改time之后的ScheduledFutureTask放回DelayQueue中(DelayQueue.add())

Excutors自述:这,这……工具类出的问题不叫bug。虽然我偷懒不干活,还可能会OOM,但我还是一个好工具类,呜呜……

作者:是啊,其实Excutors有什么错呢?它只是一个没得感情的工具类,有错的只是不恰当地用它的人。所以,知其然且知其所以然,搞懂原理,灵活应用。我们应该像一个士兵一样,不只是会扣动扳机,还会拆解保养枪械。

我是三分恶,一个号称能文能武的全栈开发。

点赞关注不迷路,咱们下期见!


参考:

[1]. 《Java并发编程的艺术》

[2]. 讲真 这次绝对让你轻松学习线程池

[3]. 小傅哥 《Java面经手册》

[4]. 《Java并发编程之美》

[5]. 阿里巴巴《Java开发手册》

Executors:为什么阿里不待见我?的更多相关文章

  1. 附001.Docker阿里云Registry加速器配置

    一 安装配置docker 1.1 安装docker 见<002.docker版本及安装>. 1.2 配置国内阿里云加速器 见<002.docker版本及安装>. 二 配置阿里云 ...

  2. J.U.C体系进阶(一):juc-executors 执行器框架

    Java - J.U.C体系进阶 作者:Kerwin 邮箱:806857264@qq.com 说到做到,就是我的忍道! 主要内容: juc-executors 执行器框架 juc-locks 锁框架 ...

  3. java线程池,阿里为什么不允许使用Executors?

    带着问题 阿里Java代码规范为什么不允许使用Executors快速创建线程池? 下面的代码输出是什么? ThreadPoolExecutor executor = new ThreadPoolExe ...

  4. 阿里不允许使用 Executors 创建线程池!那怎么使用,怎么监控?

    作者:小傅哥 博客:https://bugstack.cn 沉淀.分享.成长,让自己和他人都能有所收获! 一.前言 五常大米好吃! 哈哈哈,是不你总买五常大米,其实五常和榆树是挨着的,榆树大米也好吃, ...

  5. 阿里技术专家十五问,真题面试刀刀见肉,快来和阿里面试官battle

    引言 2020阿里巴巴专家组出题,等你来答: 题目:如何判断两个链表是否相交 出题人:阿里巴巴新零售技术质量部 参考答案: $O(n^2)$: 两层遍历,总能发现是否相交 $O(n)$: 一层遍历,遍 ...

  6. 阿里Java开发手冊之编程规约

    对于程序猿来说,编程规范能够养成良好的编程习惯,提高代码质量,减少沟通成本.就在2月9号,阿里出了一份Java开发手冊(正式版),分为编程规约.异常日志.MySQL规约,project规约.安全规约五 ...

  7. 我的阿里之路+Java面经考点

    我的阿里之路+Java面经考点 时间:2018-03-19 23:03  来源:未知   作者:admin   点击:87次 我的2017是忙碌的一年,从年初备战实习春招,年三十都在死磕JDK源码,三 ...

  8. [源码阅读] 阿里SOFA服务注册中心MetaServer(2)

    [源码阅读] 阿里SOFA服务注册中心MetaServer(2) 目录 [源码阅读] 阿里SOFA服务注册中心MetaServer(2) 0x00 摘要 0x01 MetaServer 注册 1.1 ...

  9. JUC知识点总结(知识点见内部目录)

    目录 JUC是什么 锁 Synchronized VS Lock 实现差异 Synchronized & Lock 总结 Synchronized锁的对象是什么 生产者&消费者 只有两 ...

随机推荐

  1. 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 百篇博客分析OpenHarmonyOS | v2.07

    百篇博客系列篇.本篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核资源 | 51.c.h .o 进程管理相关篇为: v02.xx 鸿蒙内核源码分析(进程管理篇) | 谁在管理内核 ...

  2. 华为云计算IE面试笔记-华为云计算解决方案业务迁移支持哪些迁移?有哪些特点?请描述基本的业务交付流程、业务迁移流程和原则。

    1. 迁移场景:华为云计算解决方案按照源端环境来说,支持P2V.V2V(P2V:物理设备(操作系统及其上的应用软件和数据)迁移到华为虚拟化平台.V2V:其他厂商的虚拟化平台迁移到华为虚拟化平台.)以及 ...

  3. 羽夏逆向破解日记簿——RunAsDate的实现原理分析

    前言   RunAsDate是一个小工具,允许您在指定的日期和时间运行程序,不过有人用它来破解有时间限制了.此实用程序不会更改计算机的当前系统日期和时间,但只会将指定的日期/时间注入所需的应用程序.该 ...

  4. AT3950-[AGC022E]Median Replace【贪心,dp】

    正题 题目链接:https://www.luogu.com.cn/problem/AT3950 题目大意 一个包含\(?,0,1\)的长度为奇数的序列,把\(?\)替换为\(0/1\).每次可以选择三 ...

  5. YbtOJ#912-神秘语言【结论,欧拉定理】

    正题 题目链接:http://www.ybtoj.com.cn/problem/912 题目大意 给出\(L,R\),求有多少长度在\([L,R]\)之间的字符串满足依次取出所有偶数位置的放在最前面后 ...

  6. easy-rule 学习

    Easyrule是个规则引擎,类似于drools,我们来熟悉一下这个东西 [ ] 一个简单实例规则,这个规则会被一直触发,然后行为是打印helloWorld @Rule(name="hell ...

  7. 架构师必备:MySQL主从同步原理和应用

    日常工作中,MySQL数据库是必不可少的存储,其中读写分离基本是标配,而这背后需要MySQL开启主从同步,形成一主一从.或一主多从的架构,掌握主从同步的原理和知道如何实际应用,是一个架构师的必备技能. ...

  8. div标签的理解

    在HTML里面,div标签是一个块状元素,不会和其他元素排列在同一行,会默认和下面的元素换行,但是如果我们需要把几个div标签排在同一行,需要怎么做? 第一种:修改块状元素 源码: <div i ...

  9. 01_vue实例_数据_方法

    创建实例 var vm = new Vue( { //选项 } ) 数据对象 // 我们的数据对象 var data = { a: 1 } // 该对象被加入到一个 Vue 实例中 var vm = ...

  10. elasticsearch的bulk(批量)操作

    在es中我们可能会有这么一种需求,即有时需要批量向es中插入或更新或删除数据,如果一条一条数据的操作,那么速度必然很慢,那么es的bulk api就可以派上用场. delete 删除操作,只需要写一个 ...