目录
ThreadPoolExecutor重要参数
poolSize、corePoolSize、maximumPoolSize
四种线程池
newFixedThreadPool
newCachedThreadPool
newSingleThreadExecutor
newScheduledThreadPool
阻塞队列
ThreadPoolExecutor重要参数
ThreadPoolExecutor有几个重要的成员变量:keepAliveTime
、allowCoreThreadTimeOut
、poolSize
、corePoolSize
、maximumPoolSize
。下面分别介绍一下:
corePoolSize
:线程池的基本大小。下面会解释什么是基本大小。maximumPoolSize
:线程池中允许的最大线程数。
注意还有一个largestPoolSize
,记录了曾经出现的最大线程个数。因为setMaximumPoolSize()
可以改变最大线程数。poolSize
:线程池中当前线程的数量。- allowCoreThreadTimeOut:是否允许核心线程超时退出。
如果该值为false,且poolSize<=corePoolSize
,线程池都会保证这些核心线程处于存活状态,不会超时退出。
如果为true,则不论poolSize的大小,都允许超时退出。
如果poolSize>corePoolSize
,则该参数不论true还是false,都允许超时退出。
相关判断如下:
(poolSize > corePoolSize || allowCoreThreadTimeOut)
复制代码
- keepAliveTime: 如果一个线程处在空闲状态的时间超过了该属性值,就会因为超时而退出。是否允许超时退出则取决于上面的逻辑。
poolSize、corePoolSize、maximumPoolSize
那么poolSize
、corePoolSize
、maximumPoolSize
三者的关系是如何的呢?
当新提交一个任务时:
- 如果
poolSize<corePoolSize
,新增加一个线程处理新的任务。 - 如果
poolSize=corePoolSize
,新任务会被放入阻塞队列等待。 - 如果阻塞队列的容量达到上限,且这时
poolSize<maximumPoolSize
,新增线程来处理任务。 - 如果阻塞队列满了,且
poolSize=maximumPoolSize
,那么线程池已经达到极限,会根据饱和策略RejectedExecutionHandler拒绝新的任务。
所以通过上面的描述可知corePoolSize<=maximumPoolSize
,poolSize<=maximumPoolSize
;而poolSize
和corePoolSize
无法比较,poolSize
是有可能比corePoolSize大
的。
四种线程池
Executors提供四种线程池:
- newCachedThreadPool :缓存线程池,如果线程池长度超过处理需要,可回收空闲线程,若无可回收,则新建线程。
- newFixedThreadPool : 定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
- newScheduledThreadPool : 计划线程池,支持定时及周期性任务执行。
- newSingleThreadExecutor :单线程线程池,用唯一的线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
那么corePoolSize、maximumPoolSize在上面四种线程池中如何设定的?
通过几个newXXX函数的源码就可以知道,源码如下:
newFixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
复制代码
定长线程池的corePoolSize
、maximumPoolSize
相同。都是设定值。
newCachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
复制代码
缓存线程池corePoolSize
为0,maximumPoolSize
则是int最大值。
newSingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
复制代码
单线程线程池corePoolSize
和maximumPoolSize
都是1。
newScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
复制代码
计划线程池用的是ThreadPoolExecutor的一个子类,可以看到corePoolSize
是定义的,而maximumPoolSize
则是int最大值。
注意这里的corePoolSize
、maximumPoolSize
不是最终的,因为可以通过setCorePoolSize
和setMaximumPoolSize()
改变。
阻塞队列
上面提到阻塞队列的饱和,那么这个饱和值是多少呢?
通过上面的代码可以看到
(1) 定长线程池和单线程线程都使用LinkedBlockingQueue
,而LinkedBlockingQueue
默认的大小是int的最大值,如下:
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
复制代码
(2)计划线程池使用的是DelayedWordQueue
,它默认大小是16,但是可以动态增长,最大值则是int的最大值,如下:
private static final int INITIAL_CAPACITY = 16;
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private void grow() {
int oldCapacity = queue.length;
int newCapacity = oldCapacity + (oldCapacity >> 1); // grow 50%
if (newCapacity < 0) // overflow
newCapacity = Integer.MAX_VALUE;
queue = Arrays.copyOf(queue, newCapacity);
}
复制代码
(3)缓存线程池使用的则是SynchronousQueue
,这个比较特殊没有所谓的饱和值,而且前面也看到了缓存线程池的corePoolSize
默认是0。所以它新建一个线程与 SynchronousQueue的机制有关, 这里不展开说了,有兴趣的可以研究一下这个类。