开发规范

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

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

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

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

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

1)FixedThreadPool 和 SingleThreadPool:

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

2)CachedThreadPool 和 ScheduledThreadPool:

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

手动创建线程池的方法

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


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

参数意义

  • corePoolSize,核心线程数,即便线程空闲也会存活的数量
  • maximumPoolSize, 最大线程数,允许创建线程的最大数量
  • keepAliveTime,超过核心线程数时,空闲线程等待 keepAliveTime 时间后才进行回收
  • unit,keepAliveTime 的时间单位
  • workQueue, 任务执行前所保存到的队列,可自定义长度,FixedThreadPoolSingleThreadPool 使用的是LinkedBlockingQueue,默认最大长度是 Integer.MAX_VALUE
  • threadFactory,创建线程的工厂,可以使用 guava 的 new ThreadFactoryBuilder().setNameFormat("我的线程-task-%d").build() 来创建一个自定义名称的线程工厂
  • handler,拒绝策略的实现,当线程池异常关闭或者任务队列满时,再次提交任务则会执行拒绝策略
    • AbortPolicy,默认的策略,抛出异常
    • DiscardPolicy,直接丢弃
    • DiscardOldestPolicy,丢弃队列中最老的任务,然后尝试执行()
    • CallerRunsPolicy,由调用线程执行此任务

其它构造方法

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


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

线程的创建过程

使用线程池的 execute(Runnable command) 方法提交任务。

  • 如果当前线程数小于corePoolSize,则创建新的线程
  • 如果当前线程数大于等于 corePoolSize,则加入到工作队列workQueue中,当队列已满,则创建新的线程
  • 线程数达到 maximumPoolSize,且队列满时,将执行拒绝策略

被禁止使用的 Executors 提供的线程池创建方式

newFixedThreadPool


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

newSingleThreadExecutor


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

newCachedThreadPool


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

思考

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