线程池简介

不积跬步,无以至千里。不积小流,无以成江海。

线程池参数

1
2
3
4
5
6
7
8
源码:
public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler)

参数解释

corePoolSize: 核心线程的大小,就算没有任务执行,在不设置 allowCoreThreadTimeOut 的情况下,就会一直存活。

maximumPoolSize: 线程池最大可容纳的线程数,如果线程池中,提交的线程超过了 corePoolSize,还可以继续提交,但是不能超过 maximumPoolSize。

keepAliveTime: 当线程池中的线程数大于核心线程数量时,无用的线程等待任务执行的最大存活的时间。

unit: 时间单位的枚举,标识 keepAliveTime 的时间单位。

workQueue: 管理通过 execute 方法提交的未执行的任务的队列。

threadFactory: 线程池创建线程使用的工厂。

handler: 当线程池达到最大值,且排队的队列满了的时候,就会拒绝新的任务加入时一个处理拒绝加入的回调。

1
2
3
4
示例:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(5, 10, 3,
                TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(5)
);

上述示例中创建了一个初始线程数为5,队列容量为5,最大线程数为10,线程数超过5时无用线程最多存活3秒的线程池。

线程池创建方式

Java通过Executors(jdk1.5并发包)来创建4种线程池,虽然方便但并不推荐。本文推荐使用 ThreadPoolExecutor 构造方法来创建线程池。

Executors.newCachedThreadPool(): 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

Executors.newFixedThreadPool(): 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

Executors.newScheduledThreadPool(): 创建一个定长线程池,支持定时及周期性任务执行。

Executors.newSingleThreadExecutor(): 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

线程池执行逻辑

当线程池接受到新任务时会经历以下几个步骤:

1、判断核心线程(corePoolSize)是否已满。(核心线程未满:创建核心线程执行任务,核心线程已满进入下一步。)

2、判断队列线程(workQueue)是否已满。(队列线程未满:将任务添加到队列,队列线程已满进入下一步。)

3、判断线程池(maximumPoolSize)是否已满。(线程池未满:创建非核心线程执行任务,线程池已满:按照策略处理无法执行的任务。)

为什么不推荐通过Executors来创建线程池

在阿里巴巴 Java 开发手册中禁止使用 Executors 来创建线程池。原因如下:

1、newFixedThreadPool 和 newSingleThreadExecutor: 主要问题是堆积的请求处理队列可能会耗费非常大的内存,甚至 OOM。

1
2
3
4
5
6
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
    }
1
2
3
4
5
6
7
源码:
public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

newFixedThreadPool和newSingleThreadExecutor 都创建了一个无界队列 LinkedBlockingQueuesize,是一个最大值为 Integer.MAX_VALUE 的线程阻塞队列,当添加任务的速度大于线程池处理任务的速度,可能会在队列堆积大量的请求,消耗很大的内存,甚至导致 OOM。

2、newCachedThreadPool和newScheduledThreadPool: 主要问题是线程数最大数是 Integer.MAX_VALUE,可能会创建数量非常多的线程,甚至 OOM。

1
2
3
4
5
6
源码:
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}
1
2
3
4
5
6
7
8
9
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
}

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
              new DelayedWorkQueue());
}

newCachedThreadPool 和 newScheduledThreadPool 创建的线程池允许的最大线程数是 Integer.MAX_VALUE,空闲线程存活时间为0,当添加任务的速度大于线程池处理任务的速度,可能会创建大量的线程,消耗资源,甚至导致 OOM。