1. 程式人生 > >Tomcat中常見執行緒說明

Tomcat中常見執行緒說明

本文講述了Tomcat的常見執行緒的功能、名稱、執行緒池和配置等資訊,其中原始碼來自於Tomcat 6.0.18。

Work執行緒

功能

HTTP請求的處理執行緒(非NIO)。當有新的http請求進來後,則會從執行緒池中獲得一個執行緒Work物件,呼叫Work.assign函式,將新到的http請求分配給這個執行緒。

名稱

名稱是http-[IpAddr]-[Port]-[Number],如http-0.0.0.0-8080-1

這個可以從Http11Protocol中的setName函式和Worker中的start方法得知這個命名方式。

publicString
getName(){
StringencodedAddr="";
if(getAddress()!=null){
encodedAddr=""+getAddress();
if(encodedAddr.startsWith("/"))
encodedAddr=encodedAddr.substring(1);
encodedAddr=URLEncoder.encode(encodedAddr)+"-";
}
10  return("http-
"+encodedAddr+endpoint.getPort());
11  }
12 
13 

執行緒類:JIoEndpoint.Work

在JIoEndpoint.Work的run方法中呼叫await方法等待並獲得下一個socket,傳給handle進行處理。在await方法中,如果沒有分配新的客戶端請求socket, available變數會一直false,並會迴圈呼叫wait方法阻塞自己,同時釋放Work物件的鎖,直到Acceptor執行緒獲得新的socket, 並呼叫Work.assign方法分配給該工作執行緒。 這時availble變數才為設定為true,並且await方法會返回分配的socket物件。

protectedclassWorkerimplementsRunnable{
protectedThreadthread=null;
protectedbooleanavailable=false;
protectedSocketsocket=null;
/**
10 
11  *ProcessanincomingTCP/IPconnectiononthespecifiedsocket.Any
12 
13  *exceptionthatoccursduringprocessingmustbeloggedandswallowed.
14 
15  *<b>NOTE</b>:ThismethodiscalledfromourConnector'sthread.We
16 
17  *mustassignittoourownthreadsothatmultiplesimultaneous
18 
19  *requestscanbehandled.
20 
21  *
22 
23  *@paramsocketTCPsockettoprocess
24 
25  */
26 
27  synchronizedvoidassign(Socketsocket){
28 
29  //WaitfortheProcessortogetthepreviousSocket
30 
31  while(available){
32 
33  try{
34 
35  wait();
36 
37  }catch(InterruptedExceptione){
38 
39  }
40 
41  }
42 
43  //StorethenewlyavailableSocketandnotifyourthread
44 
45  this.socket=socket;
46 
47  available=true;
48 
49  notifyAll();
50 
51  }
52 
53  /**
54 
55  *等待新分配的Socket
56 
57  */
58 
59  privatesynchronizedSocketawait(){
60 
61  //等待Connector提供新的Socket
62 
63  while(!available){
64 
65  try{
66 
67  wait();
68 
69  }catch(InterruptedExceptione){
70 
71  }
72 
73  }
74 
75  //通知Connector我們已經接收到這個Socket
76 
77  Socketsocket=this.socket;
78 
79  available=false;
80 
81  notifyAll();
82 
83  return(socket);
84 
85  }
86 
87  /**
88 
89  *後臺執行緒,監聽進入的TCP/IP連線,並傳遞給合適的處理模組
90 
91  */
92 
93  publicvoidrun(){
94 
95  //Processrequestsuntilwereceiveashutdownsignal
96 
97  //處理請求直到我們接收到shutdown訊號
98 
99  while(running){
100 
101  //等待下一個分配的socket
102 
103  Socketsocket=await();
104 
105  if(socket==null)
106 
107  continue;
108 
109  //設定socket的選項,並處理socket
110 
111  if(!setSocketOptions(socket)||!handler.process(socket)){
112 
113  //關閉socket
114 
115  try{
116 
117  socket.close();
118 
119  }catch(IOExceptione){
120 
121  }
122 
123  }
124 
125  //Finishupthisrequest
126 
127  socket=null;
128 
129  //回收執行緒
130 
131  recycleWorkerThread(this);
132 
133  }
134 
135  }
136 
137  /**
138 
139  *開啟後臺處理執行緒
140 
141  */
142 
143  publicvoidstart(){
144 
145  thread=newThread(this);
146 
147  thread.setName(getName()+"-"+(++curThreads));
148 
149  thread.setDaemon(true);
150 
151  thread.start();
152 
153  }
154 
155  }
156 
157 

所屬執行緒池

所屬執行緒池實現功能比較簡單,是內嵌到JIoEndpoint類中的實現。基本資料結構是一個工作執行緒棧JIoEndpoint.WorkerStack。

執行緒池主要屬性

curThreadsBusy:當前繁忙執行緒數

curThreads:當前工作執行緒數

maxThreads:最大工作執行緒數

執行緒池啟動

這個執行緒池實現功能比較簡單,不需要太多啟動功能。可以從JIoEndpoint類的start方法看到,啟動初始化需要做的事是分配執行緒棧worker空間。

任務分配時序圖

1

任務分配

通過JIoEndPoint中createWorkerThread方法獲得一個工作執行緒。如在工作執行緒棧workers中獲得一個執行緒物件,如果執行緒棧已經是空的,並且當前執行緒數量curThreads還小於最大執行緒數maxThreads,那麼就建立一個新的工作執行緒。然後呼叫Work.assign方法分配給工作執行緒。

protectedWorkercreateWorkerThread(){
//獲得工作執行緒棧workers的鎖
synchronized(workers){
//如果工作執行緒棧裡有執行緒則返回棧頂工作執行緒
if(workers.size()>0){
10 
11  curThreadsBusy++;
12 
13  returnworkers.pop();
14 
15  }
16 
17  //如果工作執行緒棧裡沒有執行緒,maxThreads大於0且當前執行緒數小於最大執行緒數,則建立一個新的執行緒
18 
19  if((maxThreads>0)&&(curThreads<maxThreads)){
20 
21  curThreadsBusy++;
22 
23  return(newWorkerThread());
24 
25  }else{
26 
27  //如果maxThreads小於0,則說明沒有限制,建立新的執行緒
28 
29  if(maxThreads<0){
30 
31  curThreadsBusy++;
32 
33  return(newWorkerThread());
34 
35  }else{
36 
37  return(null);
38 
39  }
40 
41  }
42 
43  }
44 
45  }
46 
47 

工作執行緒回收

JIoEndPoint中recycleWorkerThread方法是回收工作執行緒,當http請求處理完成,則呼叫該方法回收工作執行緒。該方法首先獲得worker物件鎖,然後呼叫workers.push方法將工作執行緒壓入工作執行緒棧中,接著將當前繁忙執行緒數減1,最後呼叫workers.notify方法。

protectedvoidrecycleWorkerThread(WorkerworkerThread){
synchronized(workers){
workers.push(workerThread);
curThreadsBusy--;
workers.notify();
10 
11  }
12  }

配置

在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執行緒,時序圖如下:

2

執行緒啟動

如上時序圖,在Tomcat啟動過程會呼叫JIoEndpoint類的start方法,會建立並啟動acceptorThreadCount個Acceptor執行緒。

publicvoidstart()throwsException{
//Initializesocketifnotdonebefore
if(!initialized){
init();
}
10 
11  if(!running){
12 
13  running=true;
14 
15  paused=false;
16 
17  //如果沒有配置executor執行緒池,則建立工作執行緒棧worker,就是上例中的執行緒池的工作執行緒棧。
18 
19  if(executor==null){
20 
21  workers=newWorkerStack(maxThreads);
22 
23  }
24 
25  //啟動acceptor執行緒
26 
27  for(inti=0;i<acceptorThreadCount;i++){
28 
29  ThreadacceptorThread=newThread(newAcceptor(),getName()+"-Acceptor-"+i);
30 
31  acceptorThread.setPriority(threadPriority);
32 
33  acceptorThread.setDaemon(daemon);
34 
35  acceptorThread.start();
36 
37  }
38 
39  }
40 
41  }

屬性

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執行緒。

publicsynchronizedvoidstart(){
stopThePool=false;
currentThreadCount=0;
currentThreadsBusy=0;
adjustLimits();
10 
11  pool=newControlRunnable[maxThreads];
12 
13  //啟動minSpareThreads空閒執行緒
14 
15  openThreads(minSpareThreads);
16 
17  //如果最大空閒執行緒數小於最大執行緒數,則啟動TP-Monitor執行緒
18 
19  if(maxSpareThreads<maxThreads){
20 
21  monitor=newMonitorRunnable(this);
22 
23  }
24 
25  }

任務分配

使用ThreadPool.runIt來執行新的任務,在該方法中,會呼叫findControlRunnable方法來獲得一個工作執行緒。需要注意的是呼叫方不需要呼叫額外的方法來回收執行緒。當ControlRunnable執行緒完成指定的任務會自動將執行緒回收到執行緒池中。

findControlRunnable是ThreadPool執行緒池的關鍵方法,它提供了從執行緒池中獲得一個工作執行緒,並將相應的計數調整,如 tpOpen,currentThreadsBusy。

/**
*ExecutesagivenRunnableonathreadinthepool,blockifneeded.
*/
publicvoidrunIt(ThreadPoolRunnabler){
if(null==r){
10 
11  thrownewNullPointerException();
12 
13  }
14 
15  //從執行緒池中獲得一個工作執行緒
16 
17  ControlRunnablec=findControlRunnable();
18 
19  //執行任務
20 
21  c.runIt(r);
22 
2