前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Executors:为什么阿里不待见我?

Executors:为什么阿里不待见我?

作者头像
三分恶
发布2021-11-15 15:44:57
5780
发布2021-11-15 15:44:57
举报
文章被收录于专栏:三分恶的专栏三分恶的专栏

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

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

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

禁止使用Excutors
禁止使用Excutors

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

1. newFixedThreadPool

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

看一下它的源代码实现:

代码语言:javascript
复制
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }

直接调用ThreadPoolExecutor的构造方法。

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

FixedThreadPoolexecute()运行示意图:

FixedThreadPool
FixedThreadPool

整体运行过程:

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

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

2. newSingleThreadExecutor

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

实现源码如下:

代码语言:javascript
复制
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

直接调用ThreadPoolExecutor的构造方法。

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

SingleThreadExecutor的运行流程:

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

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

3. newCachedThreadPool

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

实现源码:

代码语言:javascript
复制
    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的运行流程:

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

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

4. newScheduledThreadPool

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

实现源码:

代码语言:javascript
复制
    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

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

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

ScheduledThreadPoolExecutor执行任务的流程:

ScheduledThreadPool执行流程
ScheduledThreadPool执行流程

主要分为两大部分:

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

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

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

  • ScheduledFutureTask来作为调度任务的实现 它主要包含了3个成员变量time(任务将要被执行的具体时间)sequenceNumber(任务的序号)period(任务执行的间隔周期)
  • 使用DelayQueue作为任务队列 DelayQueue封装了了一个PriorityQueue,会对对队列中的ScheduledFutureTask进行排序,排序的优先级time>sequenceNumber。
ScheduledThreadPoolExecutor执行流程
ScheduledThreadPoolExecutor执行流程

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开发手册》

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

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

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

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

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 1. newFixedThreadPool
  • 2. newSingleThreadExecutor
  • 3. newCachedThreadPool
  • 4. newScheduledThreadPool
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档