Jetty 通过Server将连接器Connector和处理组件Handler连接起来,Connector负责监听用户连接请求,Handler负责读写I/O,解析HTTP协议以及Servlet规范。无论是Connector还是Handler,在处理多个请求和并发连接时,都需要线程池的支持。Jetty自己实现了线程池QueuedThreadPool来满足需求。
1 Jetty 线程池工作模型
Jetty线程池工作模型如下图,主要包含一个线程队列和一个任务队列,线程队列中的各个线程不断地从任务队列中获取下一个任务执行,有的任务自身可能会产生新的任务,比如新的I/O连接,因此执行该任务的线程会将新任务提交到任务队列中,线程每次执行一个任务结束后,会根据当前线程数目,进程设置允许的最大线程数目以及上一次线程回收时间来决定自身是否需要回收。主线程也是任务的生产者,例如在初始化Connector时创建的SelectSet。
2 Jetty 线程池工作过程分析
1) Jetty线程池相关属性
在QueuedThreadPool中定义了一组变量描述线程池的相关属性:
_threadsStarted : 一个AtomicInteger变量,用以记录当前活动的线程数;
_threadsIdle:一个AtoImicnteger变量,用于记录当前空闲的线程数;
_lastShrink: 一个AtomicLong变量,用于记录上次回收的时间;
_threads : 一个ConcurrentLinkedQueue ,用于表示当前活动线程队列;
_joinLock : 一个等待锁;
_jobs : 一个ArrayBlockingQueue,用于表示任务队列;
_name: 线程池名称;
_maxIdleTimeMs : 线程池最大空闲时间,单位为毫秒 , 默认为60000
_maxThreads : 线程池中允许的最大线程数,默认为254;
_minThreads : 线程池中维护的最少线程数,默认为8;
_maxQueued : 任务队列中允许的最大的任务数目,默认为-1表示没有限制,Jetty通过实现自己的BlockingArrayQueue支持没有限制的任务队列;
_priority : 线程池中线程的优先级,默认为Thread.NORM_PRIORITY;
_daemon : 线程池中线程是否是精灵线程,默认为false;
_maxStopTime : 最大的停止时间,默认为100;
_detailedDump : 线程池实现dump功能,该变量支持是否需要详细的dump信息;
2) 线程池的启动
线程池的启动过程很简单,主要有两步,初始化任务队列和初始化线程队列
任务队列初始化: 在Jetty中,调用Jetty组件的客户程序员或者是使用Jetty时,可以分别通过设置和配置_maxQueued 属性来告诉Jetty对任务队列的最大值是否有限制,默认为-1表示任务队列的大小没有任何限制,在这种情况下,Jetty使用自己实现的BlockingArrayQueue来实现任务队列,否则使用JAVA Concurrent包中ArrayBlockingQueue来实现任务队列:
_jobs=_maxQueued>0 ?new ArrayBlockingQueue<Runnable>(_maxQueued):new BlockingArrayQueue<Runnable>(_minThreads,_minThreads);
使用ArrayBlockingQueue时,_maxQueued表示在任一时刻,当前任务队列最多容纳的任务数,在初始化时即分配该大小空间,使用BlockingArrayQueue时,第一个_minThreads表示当前初始化时开辟的空间大小,第二个_minThreads表示在空间不够时增加空间的步长。因此,使用BlockingArrayQueue更节省空间,但是在增加空间时会带来额外的时间开销,并且对空间的限制不能很好的控制,在已提交任务但未处理任务数越来越多时,可能引起内存开销过大。Jetty默认使用自己实现的BlockingArra