必备知识我发现自己熬夜研究学习的知识和实际工作中需要的相差太多了。此外,项目中使用的一些框架模块已经使用了很长时间。我什至很难阅读代码的业务逻辑;给我印象最深的是有一个工具类,封装了海量HTTP请求,里面用到了线程池。那些眼花缭乱的参数让我当时很头疼。有些参数甚至不知道它们有什么用。为什么要把它们设置成这个?时间是让人猝不及防的东西,过了那么久,也就结束了。难免要知道的7个基本参数publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueworkQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler){}把源码里的注释贴上,个人补充(带有星号的参数更重要):*corePoolSize-线程池中保留的线程数,即使它们处于空闲状态,除非设置了allowCoreThreadTimeOut。核心线程相当于一个合同工,没有工作就得工作,就呆在那里。该参数表示contractworker(核心线程)的数量,int类型*maximumPoolSize-池中允许的最大线程数。除了contractworker,当工作堆积很多的时候,也可以找一些临时工帮忙,这个参数代表员工总数上限(contractworkers和临时工),int类型keepAliveTime-当线程数大于核心数时,这是冗余空闲线程在终止前等待新任务的最长时间。临时工在没有工作时会被解雇。这个参数表示他们多长时间不工作就会被解雇(销毁空闲线程),long型单位——keepAliveTime参数的时间单位。上面keepAliveTime参数的单位可以在TimeUnit枚举中选择*workQueue——执行任务前用来保存任务的队列。(后一句不用深究,可以忽略)这个队列只会保存execute方法提交的Runnable任务。任务一直在发,员工做不来,就建立一个队列来存放这些任务;有很多种,下面将介绍threadFactory——执行程序创建新线程时使用的工厂。工作人员(线程)可以在这里命名为*handler–由于达到线程边界和队列容量而阻塞执行时使用的处理程序。大部分文章都会称之为拒绝策略,直译无可厚非,但新接触者可能会因为翻译原因产生歧义;完整的意思是因为队列饱和采用的处理过程:可能被拒绝,可能被丢弃,甚至可能不拒绝,会创建一个新的线程继续运行任务,所以我们继续使用名称saturationstrategy后来,大家都知道这两个名字是一个意思。几个重要参数的要求以及它们之间的逻辑关系。如果以下情况之一为真,将抛出IllegalArgumentException。并且必须大于核心线程数。threadFactory和handler不是强制参数,都会有默认值,所以有些构造方法可能只使用另外5个参数。几个常用的任务队列为了更清楚的了解线程池,我们想做一个大概的介绍:ArrayBlockingQueue看到Array的开头,我们就知道这个队列是使用数组实现的队列。LinkedBlockingQueue以Linked开头。LinkedList大家比较熟悉。这个队列其实就是一个用链表实现的队列。有些文章会称ArrayBlockingQueue为有界队列,LinkedBlockingQueue为无界队列。我只想说:有点误导。因为两者只是底层实现不同,我们知道数组在内存中是连续的,所以需要指定大小,而链表是可以不连续的,所以理论上可以无限扩展,但不代表它必须是无界的。LinkedBlockingQueue有一个参数叫做capacity,代表队列的容量。之所以是unbounded,是因为它采用了无参结构,capacity默认为Integer.MAX_VALUE,但是就像list和map一样,你可以在最开始的capacity设置你想要的。//无参数构造publicLinkedBlockingQueue(){this(Integer.MAX_VALUE);}//预设最大容量构造publicLinkedBlockingQueue(intcapacity){if(capacity<=0)thrownewIllegalArgumentException();this.capacity=容量;last=head=newNode(null);}这个横向比较,LinkedBlockingQueue的吞吐量比ArrayBlockingQueue高,可以像ArrayBlockingQueue一样指定最大容量,也可以无界;这样的比较,LinkedBlockingQueue胜出,所以只要理解了这个逻辑,两个任务队列在比较中肯定是使用LinkedBlockingQueue。ArrayBlockingQueue的存在更像是在强调LinkedBlockingQueue更有用。(ArrayBlockingQueue:没惹你们!SynchronousQueue去掉了上面两个队列,还有这个特殊的队列,因为它没有容量,或者容量为0,每次put操作都要等待一个take操作。即它的上限在于take操作的效率,也就是worker线程的效率。当消费者足够多的时候,这个队列是最合适的队列。换句话说,如果你需要的任务数量是要处理的线程池不多,qps不高,甚至峰值也不高,以后不会有大的变化,那么恭喜你,你找到了你真正意义上的线程池,直接用Executors.newCachedThreadPool(),它完全能够满足你的需求,即使是不太在意原因的同学,也可以马上关闭页面开始使用。得了吧,我也相信这种情况还是很少见的。大部分人来打听如何找到适合自己的,是因为需要线程池“垫底”,即任务数或线程池的峰值实在撑不住。配置大家先别慌,继续往下看。阿里巴巴Java开发手册为什么不推荐使用Executors类自动生成的几个线程池?上面提到了Executors.newCachedThreadPool()。Executors相当于线程池的一个工具类。系统提供了几个预设的参数。一行代码可以创建一个线程池供开发者使用,但是_《阿里巴巴Java开发手册》_不推荐,为什么呢?我们来看看上面通过Executors类创建的线程池newCachedThreadPool://创建一个系统预设的线程池ExecutorServiceexecutorService=Executors.newCachedThreadPool();//构造函数如下publicstaticExecutorServicenewCachedThreadPool(){returnnewThreadPoolExecutor(0,Integer.MAX_VALUE,60L,TimeUnit.SECONDS,newSynchronousQueue());}我们可以看到:核心线程为0,最大线程数为MAX(可以理解为无限),任务队列使用SynchronousQueue。乍一看,好像没什么问题。为什么说明书上不推荐?手册上是这么写的,我们直接看2):什么意思?即预设参数的线程池CachedThreadPool最大线程数为Integer.MAX_VALUE。我们可以理解为最大线程数没有上限。当生产者提交的任务增多,消费者无法处理时,就不会一直添加工作线程,因为线程数没有上限,会不断添加线程,直到OOM发生。看到这里,相信你已经知道上面推荐的CachedThreadPool为什么要加那么多前置条件了。因为一旦消费者处理不过来,就有造成OOM的风险,谁敢乱用。而知道了这些之后,我们就可以从一个实例来推断其他的情况,还有什么会因为任务数量的增加而突然增加,然后也会出现OOM,没错,就是任务队列的数量。所以只要满足以下条件之一,任务无法处理时就可能会出现OOM:maximumPoolSize为Integer.MAX_VALUE(或大workQueue为无界队列(或Executors工具类预设的大量线程池),要么最大线程数为Max,要么任务队列无界,都满足以上条件,所以手册不推荐使用系统预设的线程池。手册的意思很明确,而不推荐的意思其实是:根据项目自己设置合适的参数,因为篇幅原因写在最后,我们的文章只是开头,描述一些基本参数,并引出最后一个问题。因为我觉得一篇文章的字数在2000~3000字左右比较合适,如果内容太多,可能不容易接受和吸收;不过放心,下一篇也已经完成,只剩下一点点修修补补了,很快就会贴出来。但也许有人会觉得比较冗长,但也不错。我也跟着自己的感觉。比如每个标题和内容我都看了好几遍才整理出来。我会回来得出结论,但引导一个问题一个接一个地讲故事。如果你能有所收获,那我会很开心~如果你能点赞、评论、收藏和分享,那我的动力就更足了,谢谢~