当前位置: 首页 > 科技观察

Tomcat如何修复JDK原生线程池bug?

时间:2023-03-12 13:35:58 科技观察

Web容器为了提高处理能力和并发性,一般会把处理请求的任务放到线程池中,而JDK的原生线程池天生适合CPU密集型任务,不适合我们平时的I/O密集型任务处理,所以Tomcat对其进行了改造。Tomcat线程池的原理其实ThreadPoolExecutor的参数主要有以下几个要点:限制线程数和限制队列长度。tomcat需要对这两种资源都进行限制,否则高并发下会耗尽CPU和内存。因此Tomcat的线程池传递参数://自定义任务队列taskqueue=newTaskQueue(maxQueueSize);//自定义线程工厂TaskThreadFactorytf=newTaskThreadFactory(namePrefix,daemon,getThreadPriority());//自定义线程池executor=newThreadPoolExecutor(getMinSpareThreads(),getMaxThreads(),maxIdleTime,TimeUnit.MILLISECONDS,taskqueue,tf);Tomcat对线程数也有限制,设置:核心线程数(minSpareThreads)线程池最大数(maxThreads)Tomcat线程池也有自己的特色任务处理进程通过实现自己特色的任务处理逻辑重写exe??cute方法:当第一个corePoolSize个任务到来时,有任务到来时创建一个新线程,当有任务时,将任务放入任务队列,所有线程都可以抢到。如果队列已满,当线程总数达到maximumPoolSize时创建一个临时线程,然后继续尝试将任务放入任务队列。如果缓冲区队列也已满,则插入失败。执行拒绝策略和JDK线程池的区别在第3步,Tomcat在线程总数上。当达到最大数量时,不会立即执行拒绝策略,而是再次尝试向任务队列中添加任务,添加失败后执行拒绝策略。具体是如何实施的?publicvoidexecute(Runnablecommand,longtimeout,TimeUnitunit){submittedCount.incrementAndGet();try{//调用JDK原生线程池的execute执行任务super.execute(command);}catch(RejectedExecutionExceptionrx){//总次数后threads达到maximumPoolSize,JDK原生线程池会执行默认的拒绝策略taskqueueif(!queue.force(command,timeout,unit)){submittedCount.decrementAndGet();//如果buffer队列还是满的,则插入失败,执行拒绝策略。thrownewRejectedExecutionException("...");}}}}}自定义Tomcat线程池任务队列的execute方法第一行:submittedCount.incrementAndGet();任务执行失败,抛出异常时,计数器减一:submittedCount.decrementAndGet();Tomcat线程池使用submittedCount变量来维护已经提交到线程池但还没有执行的任务数。为什么要维护这样一个变量?Tomcat的任务队列TaskQueue扩展了JDK的LinkedBlockingQueue,Tomcat赋予它一个容量,传递给父类LinkedBlockingQueue的构造函数。publicclassTaskQueueextendsLinkedBlockingQueue{publicTaskQueue(intcapacity){super(capacity);}...}容量参数由Tomcat的maxQueueSize参数设置,但是maxQueueSize的默认值是Integer.MAX_VALUE:这样,在当前数量之后threads达到核心线程数,再来task,线程池会把task加入task队列,一直成功,永远没有机会创建新线程。为此,TaskQueue重写LinkedBlockingQueue#offer,适时返回false,表示任务添加失败,此时线程池会创建一个新的线程。什么是合适的时间?publicclassTaskQueueextendsLinkedBlockingQueue{...@Override//当线程池调用任务队列的方法时,当前线程数>核心线程数publicbooleanoffer(Runnableo){//如果线程数已经达到max,不能创建新线程只能放入任务队列maxthreads>当前线程数>核心线程数//说明可以创建新线程://1.如果提交任务数<当前线程数//表示还有空闲线程,不需要创建新线程if(parent.getSubmittedCount()<=(parent.getPoolSize()))returnsuper.offer(o);//2。如果提交任务数>当前线程数//线程不够,返回false创建新线程if(parent.getPoolSize()