本文通过对 Executors 源码进行分析,介绍了JDK中各种默认线程池的原理以及适用场景,为如何设置线程池参数提供一定的参考。

newFixedThreadPool

  • 定义
    
    /** 
     * ......
     * @param nThreads the number of threads in the pool
     * @return the newly created thread pool
     * @throws IllegalArgumentException if {@code nThreads <= 0}
     */
    public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
  • 创建可容纳固定数量(nThreads)线程的线程池;
  • 每个线程的存活时间不限(0L),当线程池满后不在添加新的线程;
  • 如果线程池中所有线程都处于繁忙状态,新的任务将会进入阻塞队列(LinkedBlockingQueue);

newSingleThreadExecutor


    /**
     *......
     * @return the newly created single-threaded Executor
     */
    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
  • 创建一个只有一个线程的线程池;
  • 每个线程存活时间不限(0L);
  • 如果该线程处于繁忙状态,新的任务将会进入阻塞队列(LinkedBlockingQueue);

适用场景:一个任务一个任务执行的场景

newCachedThreadPool



    /**
     * ......
     * @return the newly created thread pool
     */
    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }
  • 创建一个可缓存线程池
  • 如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

ScheduledThreadPoolExecutor


    /**
     * Creates a new {@code ScheduledThreadPoolExecutor} with the
     * given core pool size.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @throws IllegalArgumentException if {@code corePoolSize < 0}
     */
    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
    }
  • 创建一个定长线程池,支持定时及周期性任务执行。

当我们使用完成ExecutorService之后应该关闭它,否则它里面的线程会一直处于运行状态。

举个例子,如果的应用程序是通过main()方法启动的,在这个main()退出之后,如果应用程序中的ExecutorService没有关闭,这个应用将一直运行。之所以会出现这种情况,是因为ExecutorService中运行的线程会阻止JVM关闭。

如果要关闭ExecutorService中执行的线程,我们可以调用ExecutorService.shutdown()方法。在调用shutdown()方法之后,ExecutorService不会立即关闭,但是它不再接收新的任务,直到当前所有线程执行完成才会关闭,所有在shutdown()执行之前提交的任务都会被执行。

如果我们想立即关闭ExecutorService,我们可以调用ExecutorService.shutdownNow()方法。这个动作将跳过所有正在执行的任务和被提交还没有执行的任务。但是它并不对正在执行的任务做任何保证,有可能它们都会停止,也有可能执行完成。

Q.E.D.