美文网首页Java 杂谈
为什么阿里禁止通过Executors创建线程池

为什么阿里禁止通过Executors创建线程池

作者: 赵荆州 | 来源:发表于2019-06-28 09:55 被阅读11次

Executors是通过new一个ThreadPoolExecutor来创建的线程池。来看看ThreadPoolExecutor的构造方法:

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             Executors.defaultThreadFactory(), defaultHandler);
    }

方法上的Javadoc:

     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.

解释一下:
corePoolSize :保持在线程池中的线程,哪怕这几个线程是空闲的。除非设置了allowCoreThreadTimeOut。
maximumPoolSize:线程池中允许的最大的线程数量。
keepAliveTime:多余corePoolSize 设置的线程的存活时间(比如corePoolSize 3,maximumPoolSize 10,那么剩下的7个线程将只能存活keepAliveTime)。
unit :keepAliveTime的时间单位
workQueue:保存corePoolSize 线程处理不了的任务(比如corePoolSize 3,但是有10个任务要处理,那么剩下的7个任务将存储在workQueue中)。当此队列满了之后,会再次创建不超过maximumPoolSize大小的线程来处理。

好,看完了ThreadPoolExecutor构造函数,再来看看Executors是怎么设置这些参数创建线程池的,以及会带来哪些问题。

看看Executors创建线程池的源码:

newFixedThreadPool

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

corePoolSize、maximumPoolSize 设置为相同(nThreads)大小,也就是说这个线程池内部会一直存在nThreads数量的线程。并且由于corePoolSize和maximumPoolSize 相同,keepAliveTime也就无意义了,所以设置为0L。
这个线程池使用的是LinkedBlockingQueue(无界队列)来存corePoolSize线程无法处理的任务。
那么这个线程池问题在哪里呢?
最大的问题在于workQueue使用了无界队列,当任务数多到线程池处理不过来时,任务全部进入workQueue,会消耗很大的内存,甚至OOM。

newSingleThreadExecutor

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

可以发现newSingleThreadExecutor 是 newFixedThreadPool的一个具化版本。指定了固定的nThreads:1。所以这个线程池的问题跟newFixedThreadPool一样。当任务过多时,可能出现OOM。

newCachedThreadPool

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

corePoolSize 设置为0,也就是说默认这个线程池中不会有线程创建。 使用的workQueue是SynchronousQueue,只有被消费才能继续生产。此时来了一个任务,发现没有线程来消费,于是创建一个线程来消费。并且这个线程在完成任务后最多存活60秒。如果此时来了很多任务。那么这个线程最大可创建Integer.MAX_VALUE个线程。这就是这个线程池的问题所在了,线程数量可以基本上说是无限制,可能导致资源耗尽。


Executors中还有很多创建线程池的工厂方法,或多或少都存在上述问题。通过上面的问题可以发现,要创建一个可靠的线程池,最好还是手动创建,并且合理指定corePoolSize、maximumPoolSize 、workQueue等参数。

例如:
为了避免OOM,我们可以使用有界队列ArrayBlockingQueue来替代无界队列。
为了避免线程过多资源耗尽,我们需要结合实际情况来指定一个maximumPoolSize,而不是粗暴的设置为Integer.MAX_VALUE。

new ThreadPoolExecutor(
            2,
            8,
            30L,
            TimeUnit.SECONDS,
            new ArrayBlockingQueue<>(1000),
            Executors.defaultThreadFactory(),
            new AbortPolicy());

上面这个线程池,当添加进任务时,默认创建2个线程来处理任务。如果又来了一个任务,此时会进入ArrayBlockingQueue(workQueue)排队,当workQueue满了(1000个任务)会在创建6个线程来处理任务。如果此时还有任务添加进来,则会执行AbortPolicy策略,默认是拒绝添加。当所有任务处理完成后,这6个线程在空闲30秒后将会被销毁。

实际使用时应该结合实际情况调整,并且自定义ThreadFactory以达到设置线程名称的目的。

原创,如有雷同纯属巧合。

相关文章

网友评论

    本文标题:为什么阿里禁止通过Executors创建线程池

    本文链接:https://www.haomeiwen.com/subject/joorcctx.html