Work线程
功能
HTTP请求的处理线程(非NIO)。当有新的http请求进来后,则会从线程池中获得一个线程Work对象,调用Work.assign函数,将新到的http请求分配给这个线程。
名称
名称是http-[IpAddr]-[Port]-[Number],如http-0.0.0.0-8080-1
这个可以从Http11Protocol中的setName函数和Worker中的start方法得知这个命名方式。
public String getName() {
String encodedAddr = "";
if (getAddress() != null) {
encodedAddr = "" + getAddress();
if (encodedAddr.startsWith("/" ))
encodedAddr = encodedAddr.substring(1);
encodedAddr = URLEncoder. encode(encodedAddr) + "-";
}
return ("http-" + encodedAddr + endpoint.getPort());
}
线程类:JIoEndpoint.Work
在JIoEndpoint.Work的run方法中调用await方法等待并获得下一个socket,传给handle进行处理。在await方法中,如果没有分配新的客户端请求socket, available变量会一直false,并会循环调用wait方法阻塞自己,同时释放Work对象的锁,直到Acceptor线程获得新的socket, 并调用Work.assign方法分配给该工作线程。 这时availble变量才为设置为true,并且await方法会返回分配的socket对象。
protected class Worker implements Runnable {
protected Thread thread = null;
protected boolean available = false;
protected Socket socket = null;
/**
* Process an incoming TCP/IP connection on the specified socket. Any
* exception that occurs during processing must be logged and swallowed.
* <b>NOTE</b> : This method is called from our Connector's thread. We
* must assign it to our own thread so that multiple simultaneous
* requests can be handled.
*
* @param socket TCP socket to process
*/
synchronized void assign(Socket socket ) {
// Wait for the Processor to get the previous Socket
while (available ) {
try {
wait();
} catch (InterruptedException e) {
}
}
// Store the newly available Socket and notify our thread
this.socket = socket ;
available = true ;
notifyAll();
}
/**
* 等待新分配的Socket
*/
private synchronized Socket await() {
//等待Connector提供新的Socket
while (!available ) {
try {
wait();
} catch (InterruptedException e) {
}
}
//通知Connector我们已经接收到这个Socket
Socket socket = this.socket ;
available = false ;
notifyAll();
return (socket);
}
/**
* 后台线程,监听进入的TCP/IP连接,并传递给合适的处理模块
*/
public void run() {
// Process requests until we receive a shutdown signal
//处理请求直到我们接收到shutdown信号
while (running ) {
//等待下一个分配的socket
Socket socket = await();
if (socket == null)
continue;
//设置socket的选项,并处理socket
if (!setSocketOptions(socket) || !handler.process(socket)) {
// 关闭socket
try {
socket.close();
} catch (IOException e) {
}
}
// Finish up this request
socket = null;
//回收线程
recycleWorkerThread( this);
}
}
/**
* 开启后台处理线程
*/
public void start() {
thread = new Thread(this);
thread.setName(getName() + "-" + (++curThreads));
thread.setDaemon(true);
thread.start();
}
}
所属线程池
所属线程池实现功能比较简单,是内嵌到JIoEndpoint类中的实现。基本数据结构是一个工作线程栈JIoEndpoint.WorkerStack。
线程池主要属性
curThreadsBusy:当前繁忙线程数
curThreads:当前工作线程数
maxThreads:最大工作线程数
线程池启动
这个线程池实现功能比较简单,不需要太多启动功能。可以从JIoEndpoint类的start方法看到,启动初始化需要做的事是分配线程栈worker空间。
任务分配时序图
任务分配
通过JIoEndPoint中createWorkerThread方法获得一个工作线程。如在工作线程栈workers中获得一个线程对象,如果线程栈已经是空的,并且当前线程数量curThreads还小于最大线程数maxThreads,那么就创建一个新的工作线程。然后调用Work.assign方法分配给工作线程。
protected Worker createWorkerThread() {
//获得工作线程栈workers的锁
synchronized (workers ) {
//如果工作线程栈里有线程则返回栈顶工作线程
if (workers .size() > 0) {
curThreadsBusy++;
return workers .pop();
}
//如果工作线程栈里没有线程,maxThreads大于0且当前线程数小于最大线程数,则创建一个新的线程
if ((maxThreads > 0) && (curThreads < maxThreads)) {
curThreadsBusy++;
return (newWorkerThread());
} else {
//如果maxThreads小于0,则说明没有限制,创建新的线程
if (maxThreads < 0) {
curThreadsBusy++;
return (newWorkerThread());
} else {
return (null);
}
}
}
工作线程回收
JIoEndPoint中recycleWorkerThread方法是回收工作线程,当http请求处理完成,则调用该方法回收工作线程。该方法首先获得worker对象锁,然后调用workers.push方法将工作线程压入工作线程栈中,接着将当前繁忙线程数减1,最后调用workers.notify方法。
protected void recycleWorkerThread(Worker workerThread) {
synchronized (workers ) {
workers.push(workerThread);
curThreadsBusy--;
workers.notify();
}
}
配置
在Tomcat中配置文件Server.xml中的Connector属性配置最大线程数maxThreads。
例如:
<Connector port="8080"
maxThreads="150"
……/>
Acceptor线程
功能
获得HTTP请求socket。并从工作线程池中获得一个线程,将socket分配给一个工作线程。
名称
http-[IPAddr]-[Port]-Acceptor-[Number],如http-0.0.0.0-8080-Acceptor-1
线程类:JIoEndpoint.Acceptor
所属线程池
无
启动时序图
在启动时会开启Accepter线程,时序图如下:
线程启动
如上时序图,在Tomcat启动过程会调用JIoEndpoint类的start方法,会创建并启动acceptorThreadCount个Acceptor线程。
public void start() throws Exception {
// Initialize socket if not done before
if (!initialized ) {
init();
}
if (!running ) {
running = true ;
paused = false ;
//如果没有配置executor线程池,则创建工作线程栈worker, 就是上例中的线程池的工作线程栈。
if (executor == null) {
workers = new WorkerStack(maxThreads);
}
//启动acceptor线程
for (int i = 0; i < acceptorThreadCount; i++) {
Thread acceptorThread = new Thread(new Acceptor(), getName() + "-Acceptor-" + i);
acceptorThread.setPriority( threadPriority);
acceptorThread.setDaemon( daemon);
acceptorThread.start();
}
}
}
属性
acceptorThreadCount:开启的acceptor线程数,从源码看到这个值并没有通过配置设置,而是固定的值为1
配置
无
Main主线程
功能
完成装配、初始化和启动,之后会开启SocketServer,并循环等待命令,如shutdown。
名称:Main
线程类:Main主线程
所属线程池:
无
catalina-exec线程
功能
StandardThreadExecutor的工作线程,功能和Work线程类似。如果为Connector配置了Executor,则会使用该线程处理http请求。
线程类:ThreadPoolExecutor.Work
所属线程池:StandardThreadExecutor
类名是org.apache.catalina.core.StandardThreadExecutor,该线程池类通过代理设计模式对Java Concurrent包中的线程池ThreadPoolExecutor进行简单的封装。并实现了Lifecycle接口,以及增加了发送消息的功能。
属性
minSpareThreads:最小空闲线程数
maxThreads:最大线程数
maxIdleTime:最大空闲时间
配置
在Server.xml文件中配置Executor节点,支持如下属性,
Name | Executor的名称 |
namePrefix | 工作线程前缀 |
maxThreads | 最大线程数 |
minSpareThreads | 最小空闲线程数 |
maxIdleTime | 最大空闲时间 |
并在Connector节点配置executor,并指定为Executor的名称。
例如:
<Executor name="tomcatThreadPool" namePrefix="catalina-exec-" maxThreads="150" minSpareThreads="4" maxIdleTime="200"/>
<Connector Address="0.0.0.0" port="8080" protocol="HTTP/1.1"executor="tomcatThreadPool".../>
TP-Processor线程
功能
AJP协议中Servlet容器的处理线程
名称
TP-Processor-[Number],例如TP-Processor-1
线程类:ThreadPool.ControlRunnable
所属线程池:org.apache.tomcat.util.threads.ThreadPool
该线程池还会启动一个TP-Monitor线程监控空闲线程。在TheadPool会有一个ControlRunnable数组保存线程池中的工作线程。使用该线程池需要先调用start方法,进行ControlRunnable数组初始化,minSpareThreads个空闲线程的创建,以及TP-Monitor线程的启动。
属性
maxThreads:最大线程数
minSpareThreads:最小空闲线程数
maxSpareThreads: 最大空闲线程数
线程池的启动
通过ThreadPool.start方法,该方法会分配线程数组pool,并打开minSpareThreads空线程。如果最大空闲线程数小于最大线程数,则启动TP-Monitor线程。
public synchronized void start() {
stopThePool=false ;
currentThreadCount = 0;
currentThreadsBusy = 0;
adjustLimits();
pool = new ControlRunnable[maxThreads];
//启动minSpareThreads空闲线程
openThreads( minSpareThreads);
//如果最大空闲线程数小于最大线程数,则启动TP-Monitor线程
if (maxSpareThreads < maxThreads) {
monitor = new MonitorRunnable(this);
}
}
任务分配
使用ThreadPool.runIt来运行新的任务,在该方法中,会调用findControlRunnable方法来获得一个工作线程。需要注意的是调用方不需要调用额外的方法来回收线程。当ControlRunnable线程完成指定的任务会自动将线程回收到线程池中。
findControlRunnable是ThreadPool线程池的关键方法,它提供了从线程池中获得一个工作线程,并将相应的计数调整,如 tpOpen,currentThreadsBusy。
/**
* Executes a given Runnable on a thread in the pool, block if needed.
*/
public void runIt(ThreadPoolRunnable r) {
if(null == r) {
throw new NullPointerException();
}
//从线程池中获得一个工作线程
ControlRunnable c = findControlRunnable();
//运行任务
c.runIt(r);
}
private ControlRunnable findControlRunnable() {
ControlRunnable c= null;
if ( stopThePool ) {
throw new IllegalStateException();
}
//从线程池中获得一个空闲线程
synchronized(this ) {
//当前繁忙线程和当前线程数相同,则表示所有的开启线程都是繁忙的。
while (currentThreadsBusy == currentThreadCount) {
//如果当前线程数比最大线程数小
if (currentThreadCount < maxThreads) {
// Not all threads were open,
// Open new threads up to the max number of idel threads
int toOpen = currentThreadCount + minSpareThreads;
openThreads(toOpen);
} else {
logFull(log, currentThreadCount, maxThreads );
//线程数已经满了,等待线程成为空闲线程
try {
this.wait();
}
// was just catch Throwable -- but no other
// exceptions can be thrown by wait, right
// So we catch and ignore this one, since
// it'll never actually happen, since nowhere
// do we say pool.interrupt().
catch(InterruptedException e) {
log.error("Unexpected exception" , e);
}
if( log .isDebugEnabled() ) {
log.debug("Finished waiting: CTC=" +currentThreadCount +
", CTB=" + currentThreadsBusy );
}
// Pool was stopped. Get away of the pool.
if( stopThePool ) {
break;
}
}
}
//线程池已经关闭,离开线程池
if(0 == currentThreadCount || stopThePool) {
throw new IllegalStateException();
}
//到了这里,表示有空闲线程可用
//取出数组pool中最后一个线程
int pos = currentThreadCount - currentThreadsBusy - 1;
c = pool[pos];
pool[pos] = null;
//繁忙线程数加1
currentThreadsBusy++;
}
return c;
}
/**
*开启线程
* @param toOpen 我们将要开启的线程数
*/
protected void openThreads(int toOpen) {
if(toOpen > maxThreads ) {
toOpen = maxThreads;
}
//创建空闲线程
for(int i = currentThreadCount ; i < toOpen ; i++) {
//需要减去currentThreadsBusy, 因为繁忙线程已经从pool数组中移出
pool[i - currentThreadsBusy ] = new ControlRunnable( this);
}
currentThreadCount = toOpen;
}
工作线程回收
通过ThreadPool.returnController方法回收线程。该方法会将繁忙线程数currentThreadsBusy减1,并将线程回收到线程数组中。
/**
* 将线程返还线程池
*/
protected synchronized void returnController (ControlRunnable c) {
if(0 == currentThreadCount || stopThePool) {
c.terminate();
return;
}
// atomic
currentThreadsBusy--;
//将线程回收到pool数组中
pool[currentThreadCount - currentThreadsBusy - 1] = c;
//notify会唤醒在等待线程资源
notify();
}
配置
在Server.xml文件中配置Connector属性
maxThreads | 最大线程数 |
minSpareThreads | 最小空闲线程数 |
maxSpareThreads | 最大空闲线程数 |
例如:
<Connector port="8009" protocol="AJP/1.3" redirectPort="8443" maxThreads="800" minSpareThreads="50" maxSpareThreads="500" />
TP-Monitor线程
功能
监控ThreadPool线程池的空闲线程,回收比最大空闲线程数多出的空闲线程。
线程类:ThreadPool.MonitorRunnable
/**
* 定期清理空闲线程
*/
public static class MonitorRunnable implements Runnable {
ThreadPool p;
Thread t;
int interval =WORK_WAIT_TIMEOUT;
boolean shouldTerminate ;
MonitorRunnable(ThreadPool p) {
this.p =p;
this.start();
}
public void start() {
shouldTerminate = false ;
t = new Thread(this);
t.setDaemon( p.getDaemon() );
t.setName( p.getName() + "-Monitor");
t.start();
}
public void setInterval(int i ) {
this.interval =i;
}
public void run() {
while(true ) {
try {
//Wait一段时间
synchronized(this ) {
this.wait(interval );
}
// Check if should terminate.
// termination happens when the pool is shutting down.
if(shouldTerminate ) {
break;
}
//回收空闲线程
p.checkSpareControllers();
} catch(Throwable t) {
ThreadPool. log.error("Unexpected exception" , t);
}
}
}
public void stop() {
this.terminate();
}
/** 停止monitor线程
*/
public synchronized void terminate() {
shouldTerminate = true ;
this.notify();
}
}
ThreadPool.checkSpareControllers方法,用来被TP-Monitor线程调用回收工作线程。
/**
* 被TP-Monitor线程用来回收线程
*/
protected synchronized void checkSpareControllers() {
if(stopThePool ) {
return;
}
//如果当前空闲线程数大于最大空闲线程数
if((currentThreadCount - currentThreadsBusy) > maxSpareThreads) {
//回收比最大空闲线程数多出的空闲线程
int toFree = currentThreadCount -
currentThreadsBusy -
maxSpareThreads;
for(int i = 0 ; i < toFree ; i++) {
ControlRunnable c = pool[currentThreadCount - currentThreadsBusy - 1];
c.terminate();
pool[currentThreadCount - currentThreadsBusy - 1] = null;
currentThreadCount --;
}
}
}
所属线程池
ThreadPool线程池
ContainerBackgroundProcessor线程
功能
容器后台线程,只有设置backgroundProcessorDelay大于0的容器才会启动ContainerBackgroundProcessor线程。该线程会调用当前容器的backgroundProcess方法,并且递归调用 backgroundProcessorDelay值小于等于0的子容器的方法。
从源码中看到只有StandardEngine设置了这个backgroundProcessorDelay值为10,所以只有StandardEngine容器启动ContainerBackgroundProcessor线程, 而其它StandardHost, StandardContext设置的值都是-1。
/**
* 创建一个新的StandardEngine组件,并绑定默认的基础Valve。
*/
public StandardEngine() {
super();
pipeline.setBasic(new StandardEngineValve());
/* Set the jmvRoute using the system property jvmRoute */
try {
setJvmRoute(System. getProperty("jvmRoute"));
} catch(Exception ex) {
}
// Engine将拥有reloading线程
backgroundProcessorDelay = 10;
}
线程类:ContainerBase.ContainerBackgroundProcessor
/**
* ContainerBase的保护线程类,调用当前容器的backgroundProcess方法,并在一个固定延时后,
* 用它的子容器的backgroundProcess方法
*/
protected class ContainerBackgroundProcessor implements Runnable {
public void run() {
while (!threadDone ) {
try {
Thread. sleep(backgroundProcessorDelay * 1000L);
} catch (InterruptedException e) {
;
}
if (!threadDone ) {
//获得当前容器,作为父容器
Container parent = (Container) getMappingObject();
ClassLoader cl =
Thread. currentThread().getContextClassLoader();
if (parent.getLoader() != null) {
cl = parent.getLoader().getClassLoader();
}
//处理父容器和所有的子容器
processChildren(parent, cl);
}
}
}
//处理父容器和所有的子容器
protected void processChildren(Container container, ClassLoader cl) {
try {
//如果父容器的loader不为null,则将当前线程的上下文类加载器contextClassLoader设置为父容器
//的loader的类加载器
if (container.getLoader() != null) {
Thread. currentThread().setContextClassLoader
(container.getLoader().getClassLoader());
}
//调用父容器的backgroundProcess方法
container.backgroundProcess();
} catch (Throwable t) {
log.error("Exception invoking periodic operation: " , t);
} finally {
Thread. currentThread().setContextClassLoader(cl);
}
//获得父容器的所有子容器
Container[] children = container.findChildren();
for (int i = 0; i < children.length; i++) {
//如果子容器的backgroundProcessorDelay小于等于0,则递归处理子容器
if (children[i].getBackgroundProcessorDelay() <= 0) {
processChildren(children[i], cl);
}
}
}
}