Executors的“罪与罚”
在上一篇文章Java并发 之 线程池系列 (1) 让多线程不再坑爹的线程池中,我们介绍了使用JDK concurrent包下的工厂和工具类Executors
来创建线程池常用的几种方法:
//创建固定线程数量的线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(10);
//创建一个线程池,该线程池会根据需要创建新的线程,但如果之前创建的线程可以使用,会重用之前创建的线程
ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
//创建一个只有一个线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
诚然,这种创建线程池的方法非常简单和方便。但仔细阅读源码,却把我吓了一条: 这是要老子的命啊!
我们前面讲过,如果有新的请求过来,在线程池中会创建新的线程处理这些任务,一直创建到线程池的最大容量(Max Size)为止;超出线程池的最大容量的Tasks,会被放入阻塞队列(Blocking
Queue)进行等待,知道有线程资源释放出来为止;要知道的是,阻塞队列也是有最大容量的,多余队列最大容量的请求不光没有获得执行的机会,连排队的资格都没有!
那这些连排队的资格都没有的Tasks怎么处理呢?不要急,后面在介绍ThreadPoolExecutor
的拒绝处理策略(Handler Policies for Rejected Task)的时候会详细介绍。
说到这里你也许有写疑惑了,上面这些东西,我通常使用Executors
的时候没有指定过啊。是的,因为Executors
很“聪明”地帮我们做了这些事情。
Executors的源码
我们看下Executors
的newFixedThreadPool
和newSingleThreadExecutor
方法的源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(
nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(
1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
其实它们底层还是通过ThreadPoolExecutor
来创建ExecutorService
的,这里对妻子的参数先不作介绍,下面会详细讲,这里只说一下new LinkedBlockingQueue<Runnable>()
这个参数。
LinkedBlockingQueue
就是当任务数大于线程池的线程数的时候的阻塞队列,这里使用的是无参构造,我们再看一下构造函数:
/**
* Creates a {@code LinkedBlockingQueue} with a capacity of
* {@link Integer#MAX_VALUE}.
*/
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
我们看到阻塞队列的默认大小竟然是Integer.MAX_VALUE
!
如果不做控制,拼命地往阻塞队列里放Task,分分钟“Out of Memory”啊!
还有更绝的,newCachedThreadPool
方法:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(
0, Integer.MAX_VALUE,
60L, TimeUnit