zoukankan      html  css  js  c++  java
  • 为什么不推荐通过Executors直接创建线程池

     

    文章目录

    • 一、缓存队列 LinkedBlockingQueue 没有设置固定容量大小
      • 1.1、Executors.newFixedThreadPool()
      • 1.2、Executors.newSingleThreadExecutor()
      • 总结:
    • 二 、最大线程数量是 Integer.MAX_VALUE
      • 2.1、Executors.newCachedThreadPool()
      • 2.2、Executors.newScheduledThreadPool()
      • 总结:
    • 三、拒绝策略不能自定义(这个不是重点)
    • 四、创建线程 或 线程池时请指定有意义的线程名称,方便出错时回溯(这个不是重点)

    阿里发布的 Java开发手册中强制线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式。这是为什么?
    有4个原因,前2个是主要原因。具体如下:

    一、缓存队列 LinkedBlockingQueue 没有设置固定容量大小

    1.1、Executors.newFixedThreadPool()

    创建固定大小的线程池

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

    ThreadPoolExecutor 部分参数:

    • corePoolSize :线程池中核心线程数的最大值。此处为 nThreads个。
    • maximumPoolSize :线程池中能拥有最多线程数 。此处为 nThreads 个。
    • LinkedBlockingQueue 用于缓存任务的阻塞队列 。 此处没有设置容量大小,默认是 Integer.MAX_VALUE,可以认为是无界的。

    问题分析:
    从源码中可以看出, 虽然表面上 newFixedThreadPool() 中定义了 核心线程数 和 最大线程数 都是固定 nThreads 个,但是当 线程数量超过 nThreads 时,多余的线程会保存到 LinkedBlockingQueue 中,而 LinkedBlockingQueue 没是无界的,导致其无限增大,最终内存撑爆。

    1.2、Executors.newSingleThreadExecutor()

    创建单个线程池 ,线程池中只有一个线程。

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

    创建单个线程池 ,线程池中只有一个线程。

    优点: 创建一个单线程的线程池,保证线程的顺序执行 ;
    缺点: 与 newFixedThreadPool() 相同。

    总结:

    newFixedThreadPool()、newSingleThreadExecutor() 底层代码 中 LinkedBlockingQueue 没有设置容量大小,默认是 Integer.MAX_VALUE, 可以认为是无界的。线程池中 多余的线程会被缓存到 LinkedBlockingQueue中,最终内存撑爆。

    二 、最大线程数量是 Integer.MAX_VALUE

    2.1、Executors.newCachedThreadPool()

    缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量

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

    ThreadPoolExecutor 部分参数:

    • corePoolSize :线程池中核心线程数的最大值。此处为 0 个。
    • maximumPoolSize :线程池中能拥有最多线程数 。此处为 Integer.MAX_VALUE 。可以认为是无限大 。

    优点: 很灵活,弹性的线程池线程管理,用多少线程给多大的线程池,不用后及时回收,用则新建 ;
    缺点: 从源码中可以看出,SynchronousQueue() 只能存一个队列,可以认为所有 放到 newCachedThreadPool() 中的线程,不会缓存到队列中,而是直接运行的, 由于最大线程数是 Integer.MAX_VALUE ,这个数量级可以认为是无限大了, 随着执行线程数量的增多 和 线程没有及时结束,最终会将内存撑爆

    2.2、Executors.newScheduledThreadPool()

    创建固定大小的线程,可以延迟或定时的执行任务

    public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
    }
    
    // ScheduledThreadPoolExecutor 类的源码:
    public ScheduledThreadPoolExecutor(int corePoolSize,
                                       ThreadFactory threadFactory) {
        super(corePoolSize, Integer.MAX_VALUE, 0, TimeUnit.NANOSECONDS,
              new DelayedWorkQueue(), threadFactory);
    }
    

    优点: 创建一个固定大小线程池,可以定时或周期性的执行任务 ;
    缺点: 与 newCachedThreadPool() 相同。

    总结:

    newCachedThreadPool()、newScheduledThreadPool() 的 底层代码 中 的 最大线程数(maximumPoolSize) 是 Integer.MAX_VALUE,可以认为是无限大,如果线程池中,执行中的线程没有及时结束,并且不断地有线程加入并执行,最终会将内存撑爆。

    三、拒绝策略不能自定义(这个不是重点)

    它们统一缺点:不支持自定义拒绝策略。

    Executors 底层其实是使用的 ThreadPoolExecutor 的方式 创建的,但是使用的是 ThreadPoolExecutor 的默认策略,即 AbortPolicy

    //默认策略
     private static final RejectedExecutionHandler defaultHandler =
            new AbortPolicy();
    
    //构造函数
    public ThreadPoolExecutor(int corePoolSize,
                                 int maximumPoolSize,
                                 long keepAliveTime,
                                 TimeUnit unit,
                                 BlockingQueue<Runnable> workQueue) {
    	this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
                Executors.defaultThreadFactory(), defaultHandler);
    }
     

    四、创建线程 或 线程池时请指定有意义的线程名称,方便出错时回溯(这个不是重点)

    这个第四条原因参考了: https://blog.csdn.net/w605283073/article/details/80259493

    欢迎关注微信公众号:shoshana

  • 相关阅读:
    机器学习三--分类--邻近取样(Nearest Neighbor)
    机器学习二——分类算法--决策树DecisionTree
    机器学习一--基本概念
    python学习--常用正则表达式整理
    python学习--字符编码问题
    python学习--字符串处理相关方法
    python学习--如何在一个for循环中迭代多个可迭代对象(串行,并行)
    python学习--如何对迭代器进行切片操作
    List对象去重
    List 简单升降序实现
  • 原文地址:https://www.cnblogs.com/shoshana-kong/p/14030453.html
Copyright © 2011-2022 走看看