Bootstrap

ThreadPoolExecutor 线程池使用

开发规范

阿里巴巴开发规范中指出了3点和线程使用相关的强制措施。

1、创建线程或线程池时请指定有意义的线程名称,方便出错时回溯。

2、线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。

3、线程池不允许使用 Executors 去创建,而是通过 ThreadPoolExecutor 的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险。

不允许使用 Executors 创建的原因如下。

1)FixedThreadPool 和 SingleThreadPool:

允许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

2)CachedThreadPool 和 ScheduledThreadPool:

允许的创建线程数量为 Integer.MAX_VALUE,可能会创建大量的线程,从而导致 OOM。

手动创建线程池的方法

提供了几个构造方法,拿参数最全的举例说明。

public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

参数意义

  • ,核心线程数,即便线程空闲也会存活的数量

  • , 最大线程数,允许创建线程的最大数量

  • ,超过核心线程数时,空闲线程等待 keepAliveTime 时间后才进行回收

  • ,keepAliveTime 的时间单位

  • , 任务执行前所保存到的队列,可自定义长度, 和 使用的是,默认最大长度是 Integer.MAX_VALUE

  • ,创建线程的工厂,可以使用 guava 的 来创建一个自定义名称的线程工厂

  • ,拒绝策略的实现,当线程池异常关闭或者任务队列满时,再次提交任务则会执行拒绝策略

  • ,默认的策略,抛出异常

  • ,直接丢弃

  • ,丢弃队列中最老的任务,然后尝试执行()

  • ,由调用线程执行此任务

其它构造方法

最少参数的构造方法使用默认线程工厂和默认拒绝策略。

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

线程的创建过程

使用线程池的 方法提交任务。

  • 如果当前线程数小于,则创建新的线程

  • 如果当前线程数大于等于 ,则加入到工作队列中,当队列已满,则创建新的线程

  • 线程数达到 ,且队列满时,将执行拒绝策略

思考

在有大量任务执行的场景,如果使用不恰当的创建线程池的方式,可能会引起线上的问题,例如 OOM。所以阿里强制程序员使用自定义线程池,创建线程池时清楚地指定好各个参数,避免深度的封装类导致不知道实现细节而踩坑。