tomcat线程池和jdk线程池区别
概述
- 线程池是什么,为什么要线程池
- jdk有哪些线程池和原理
- 第三方中间件的线程池特点
- tomcat线程池
- quartz线程池
- 线程池展望
线程池是什么,为什么要线程池
我们在项目启动后,因为CPU是多核的,为了充分利用CPU,可能需要启动多个线程去处理任务。频繁的创建销毁线程是有开销的。所以需要创建线程后缓存住,让线程可以频繁的处理各种任务。这个时候就需要用线程池。线程池一般会缓存住N个线程,然后往线程池里面扔任务Runnable,线程不断的处理任务。处理完了后停留在那里等待。
jdk有哪些线程池和原理
jdk线程池主要入口是在java.util.concurrent.Executors,其中包含
-
Executors.newFixedThreadPool 固定核心数的线程池,有多余任务往无界队列中放
-
Executors.newSingleThreadExecutor 单线程的线程池,有多余任务往无界队列中放
-
Executors.newCachedThreadPool 有任务来就开辟新的线程,不缓存任务,不缓存线程。
-
Executors.newSingleThreadScheduledExecutor 单线程的延迟线程池,也就是说可以指定任务N秒后周期性执行。
-
Executors.newScheduledThreadPool N线程的延迟线程池,也就是说可以指定任务N秒后周期性执行。
* 这个线程池内部依赖于ScheduledThreadPoolExecutor,队列依赖于ScheduledThreadPoolExecutor.DelayedWorkQueue,这个队列在take的时候,会根据你添加任务的时候的时间做一个awaitNanos(delay),代码见该类的take方法。故而实现了延迟执行。 -
Executors.unconfigurableExecutorService
线程池的主要原理是在ThreadPoolExecutor这个类中。ThreadPoolExecutor有以下概念
-
corePoolSize
-
maximumPoolSize
-
keepAliveTime
-
workQueue
这四个概念一起解释,corePoolSize指的是线程池核心数,当有任务进来的时候,如果开辟的线程少于该值,就不断开新线程去处理,直到该值。如果到了该值之后,就把新的任务存放在队列中,也就是workQueue中,去存放的过程会成功和失败,如果成功就存放在队列中了,如果失败,就继续开辟线程去执行。直到线程数等于maximumPoolSize,便不再开新线程。如果队列满了,线程也到最大值了,这个时候,就进行拒绝策略。
-
AbortPolicy 默认策略,抛出异常RejectedExecutionException。
-
DiscardPolicy 什么都不做
-
DiscardOldestPolicy 把任务头丢掉,然后再把任务加入队列
-
CallerRunsPolicy 主线程自己去执行任务
-
RejectHandler 自己实现
原理大致说明。ThreadPoolExecutor这个类除了参数那些参数外,还有HashSet<Worker> workers,参数,该参数和线程一一对应,也就是说线程池里面的线程就是worker,当任务进来后会和corePoolSize比较,继而新建或者申请worker。新建的线程会不断的从任务队列去拿任务,拿任务有超时时间,就是刚才说到的keepAliveTime,到达时间后,如果说当前线程数大于corePollSize,则自己退出该线程。没有大于则继续循环运行,继续循环运行就是在死循环中去拿队列。
第三方中间件的线程池特点
tomcat线程池
tomcat的线程池是StandardThreadExecutor类,该类的属性也是ThreadPoolExecutor,不过是把ThreadPoolExecutor动态设置参数暴露出来了。所以StandardThreadExecutor类可以动态的修改核心数,最大数。并且该类的阻塞队列使用的是继承自LinkedBlockingQueue的TaskQueue。TaskQueue的核心在于,只要运行当前池子的线程小于maximumPoolSize,就拒绝你加入队列。我们知道线程的数量和IO密集有关系,tomcat这种IO密集型的中间件,适合更多的线程。这里的线程数是maxThreads,该值默认200,tomcat可以配置。
public boolean offer(Runnable o) {
//这个类是TaskQueue的的方法,我们知道,线程池,是先加入队列,失败后再开线程直到max数。
if (parent==null) return super.offer(o);
// 若运行线程为最大线程数,说明线程已经最大了,没办法,还是扔队列吧。
if (parent.getPoolSize() == parent.getMaximumPoolSize()) return super.offer(o);
//若任务数小于线程数,直接扔队列,扔队列后,那些闲着的线程自然会取。没必要返回false,让其再重开线程。
if (parent.getSubmittedCount()<(parent.getPoolSize())) return super.offer(o);
// 发现运行线程数少于池最大线程数,拒绝加入队列,让线程池继续开线程。
if (parent.getPoolSize()<parent.getMaximumPoolSize()) return false;
//if we reached here, we need to add it to the queue
return super.offer(o);
}
quartz线程池
quartz线程池是在org.quartz.simpl.SimpleThreadPool
该线程池非常简单,初始化的时候新建N个线程。这个线程是在线程池初始化的时候就新建。然后把线程放在availWorkers中,如果有任务进来,就从availWorkers拿出1个线程放进busyWorkers,然后把任务给该线程执行。执行完了之后返回给availWorkers中。如果线程满了,主线程扔任务的时候,会检查availWorkers的size,当size<1的时候,停半秒继续检查availWorkers直到有可用才运行任务。
网友评论