http://blog.csdn.NET/jeff_fangji/article/details/41786205
本文講述了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方法得知這個命名方式。
1 | public String getName() { |
2 | String encodedAddr = ""; |
3 | if (getAddress() != null) { |
4 | encodedAddr = "" + getAddress(); |
5 | if (encodedAddr.startsWith("/" )) |
6 | encodedAddr = encodedAddr.substring(1); |
7 | encodedAddr = URLEncoder. encode(encodedAddr) + "-"; |
8 | } |
9 | |
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對象。
1 | protected class Worker implements Runnable { |
2 | |
3 | protected Thread thread = null; |
4 | |
5 | protected boolean available = false; |
6 | |
7 | protected Socket socket = null; |
8 | |
9 | /** |
10 | |
11 | * Process an incoming TCP/IP connection on the specified socket. Any |
12 | |
13 | * exception that occurs during processing must be logged and swallowed. |
14 | |
15 | * <b>NOTE</b> : This method is called from our Connector's thread. We |
16 | |
17 | * must assign it to our own thread so that multiple simultaneous |
18 | |
19 | * requests can be handled. |
20 | |
21 | * |
22 | |
23 | * @param socket TCP socket to process |
24 | |
25 | */ |
26 | |
27 | synchronized void assign(Socket socket ) { |
28 | |
29 | // Wait for the Processor to get the previous Socket |
30 | |
31 | while (available ) { |
32 | |
33 | try { |
34 | |
35 | wait(); |
36 | |
37 | } catch (InterruptedException e) { |
38 | |
39 | } |
40 | |
41 | } |
42 | |
43 | // Store the newly available Socket and notify our thread |
44 | |
45 | this.socket = socket ; |
46 | |
47 | available = true ; |
48 | |
49 | notifyAll(); |
50 | |
51 | } |
52 | |
53 | /** |
54 | |
55 | * 等待新分配的Socket |
56 | |
57 | */ |
58 | |
59 | private synchronized Socket await() { |
60 | |
61 | //等待Connector提供新的Socket |
62 | |
63 | while (!available ) { |
64 | |
65 | try { |
66 | |
67 | wait(); |
68 | |
69 | } catch (InterruptedException e) { |
70 | |
71 | } |
72 | |
73 | } |
74 | |
75 | //通知Connector我們已經接收到這個Socket |
76 | |
77 | Socket socket = 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 | public void run() { |
94 | |
95 | // Process requests until we receive a shutdown signal |
96 | |
97 | //處理請求直到我們接收到shutdown信號 |
98 | |
99 | while (running ) { |
100 | |
101 | //等待下一個分配的socket |
102 | |
103 | Socket socket = 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 (IOException e) { |
120 | |
121 | } |
122 | |
123 | } |
124 | |
125 | // Finish up this request |
126 | |
127 | socket = null; |
128 | |
129 | //回收線程 |
130 | |
131 | recycleWorkerThread( this); |
132 | |
133 | } |
134 | |
135 | } |
136 | |
137 | /** |
138 | |
139 | * 開啟后台處理線程 |
140 | |
141 | */ |
142 | |
143 | public void start() { |
144 | |
145 | thread = new Thread(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空間。
任務分配時序圖
任務分配
通過JIoEndPoint中createWorkerThread方法獲得一個工作線程。如在工作線程棧workers中獲得一個線程對象,如果線程棧已經是空的,並且當前線程數量curThreads還小於最大線程數maxThreads,那么就創建一個新的工作線程。然后調用Work.assign方法分配給工作線程。
1 | protected Worker createWorkerThread() { |
2 | |
3 | //獲得工作線程棧workers的鎖 |
4 | |
5 | synchronized (workers ) { |
6 | |
7 | //如果工作線程棧里有線程則返回棧頂工作線程 |
8 | |
9 | if (workers .size() > 0) { |
10 | |
11 | curThreadsBusy++; |
12 | |
13 | return workers .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方法。
1 | protected void recycleWorkerThread(Worker workerThread) { |
2 | |
3 | synchronized (workers ) { |
4 | |
5 | workers.push(workerThread); |
6 | |
7 | curThreadsBusy--; |
8 | |
9 | 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線程,時序圖如下:
線程啟動
如上時序圖,在Tomcat啟動過程會調用JIoEndpoint類的start方法,會創建並啟動acceptorThreadCount個Acceptor線程。
1 | public void start() throws Exception { |
2 | |
3 | // Initialize socket if not done before |
4 | |
5 | if (!initialized ) { |
6 | |
7 | init(); |
8 | |
9 | } |
10 | |
11 | if (!running ) { |
12 | |
13 | running = true ; |
14 | |
15 | paused = false ; |
16 | |
17 | //如果沒有配置executor線程池,則創建工作線程棧worker, 就是上例中的線程池的工作線程棧。 |
18 | |
19 | if (executor == null) { |
20 | |
21 | workers = new WorkerStack(maxThreads); |
22 | |
23 | } |
24 | |
25 | //啟動acceptor線程 |
26 | |
27 | for (int i = 0; i < acceptorThreadCount; i++) { |
28 | |
29 | Thread acceptorThread = new Thread(new Acceptor(), 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線程。
1 | public synchronized void start() { |
2 | |
3 | stopThePool=false ; |
4 | |
5 | currentThreadCount = 0; |
6 | |
7 | currentThreadsBusy = 0; |
8 | |
9 | adjustLimits(); |
10 | |
11 | pool = new ControlRunnable[maxThreads]; |
12 | |
13 | //啟動minSpareThreads空閑線程 |
14 | |
15 | openThreads( minSpareThreads); |
16 | |
17 | //如果最大空閑線程數小於最大線程數,則啟動TP-Monitor線程 |
18 | |
19 | if (maxSpareThreads < maxThreads) { |
20 | |
21 | monitor = new MonitorRunnable(this); |
22 | |
23 | } |
24 | |
25 | } |
任務分配
使用ThreadPool.runIt來運行新的任務,在該方法中,會調用findControlRunnable方法來獲得一個工作線程。需要注意的是調用方不需要調用額外的方法來回收線程。當ControlRunnable線程完成指定的任務會自動將線程回收到線程池中。
findControlRunnable是ThreadPool線程池的關鍵方法,它提供了從線程池中獲得一個工作線程,並將相應的計數調整,如 tpOpen,currentThreadsBusy。
1 | /** |
2 | |
3 | * Executes a given Runnable on a thread in the pool, block if needed. |
4 | |
5 | */ |
6 | |
7 | public void runIt(ThreadPoolRunnable r) { |
8 | |
9 | if(null == r) { |
10 | |
11 | throw new NullPointerException(); |
12 | |
13 | } |
14 | |
15 | //從線程池中獲得一個工作線程 |
16 | |
17 | ControlRunnable c = findControlRunnable(); |
18 | |
19 | //運行任務 |
20 | |
21 | c.runIt(r); |
22 | |
23 | } |
24 | |
25 | private ControlRunnable findControlRunnable() { |
26 | |
27 | ControlRunnable c= null; |
28 | |
29 | if ( stopThePool ) { |
30 | |
31 | throw new IllegalStateException(); |
32 | |
33 | } |
34 | |
35 | //從線程池中獲得一個空閑線程 |
36 | |
37 | synchronized(this ) { |
38 | |
39 | //當前繁忙線程和當前線程數相同,則表示所有的開啟線程都是繁忙的。 |
40 | |
41 | while (currentThreadsBusy == currentThreadCount) { |
42 | |
43 | //如果當前線程數比最大線程數小 |
44 | |
45 | if (currentThreadCount < maxThreads) { |
46 | |
47 | // Not all threads were open, |
48 | |
49 | // Open new threads up to the max number of idel threads |
50 | |
51 | |
52 | int toOpen = currentThreadCount + minSpareThreads; |
53 | |
54 | openThreads(toOpen); |
55 | |
56 | } else { |
57 | |
58 | logFull(log, currentThreadCount, maxThreads ); |
59 | |
60 | //線程數已經滿了,等待線程成為空閑線程 |
61 | |
62 | try { |
63 | |
64 | this.wait(); |
65 | |
66 | } |
67 | |
68 | // was just catch Throwable -- but no other |
69 | |
70 | // exceptions can be thrown by wait, right? |
71 | |
72 | // So we catch and ignore this one, since |
73 | |
74 | // it'll never actually happen, since nowhere |
75 | |
76 | // do we say pool.interrupt(). |
77 | |
78 | catch(InterruptedException e) { |
79 | |
80 | log.error("Unexpected exception" , e); |
81 | |
82 | } |
83 | |
84 | if( log .isDebugEnabled() ) { |
85 | |
86 | log.debug("Finished waiting: CTC=" +currentThreadCount + |
87 | |
88 | ", CTB=" + currentThreadsBusy ); |
89 | |
90 | } |
91 | |
92 | // Pool was stopped. Get away of the pool. |
93 | |
94 | if( stopThePool ) { |
95 | |
96 | break; |
97 | |
98 | } |
99 | |
100 | } |
101 | |
102 | } |
103 | |
104 | //線程池已經關閉,離開線程池 |
105 | |
106 | if(0 == currentThreadCount || stopThePool) { |
107 | |
108 | throw new IllegalStateException(); |
109 | |
110 | } |
111 | |
112 | //到了這里,表示有空閑線程可用 |
113 | |
114 | //取出數組pool中最后一個線程 |
115 | |
116 | int pos = currentThreadCount - currentThreadsBusy - 1; |
117 | |
118 | c = pool[pos]; |
119 | |
120 | pool[pos] = null; |
121 | |
122 | //繁忙線程數加1 |
123 | |
124 | currentThreadsBusy++; |
125 | |
126 | } |
127 | |
128 | return c; |
129 | |
130 | } |
131 | |
132 | /** |
133 | |
134 | *開啟線程 |
135 | |
136 | * @param toOpen 我們將要開啟的線程數 |
137 | |
138 | */ |
139 | |
140 | protected void openThreads(int toOpen) { |
141 | |
142 | if(toOpen > maxThreads ) { |
143 | |
144 | toOpen = maxThreads; |
145 | |
146 | } |
147 | |
148 | //創建空閑線程 |
149 | |
150 | for(int i = currentThreadCount ; i < toOpen ; i++) { |
151 | |
152 | //需要減去currentThreadsBusy, 因為繁忙線程已經從pool數組中移出 |
153 | |
154 | pool[i - currentThreadsBusy ] = new ControlRunnable( this); |
155 | |
156 | } |
157 | |
158 | currentThreadCount = toOpen; |
159 | |
160 | } |
工作線程回收
通過ThreadPool.returnController方法回收線程。該方法會將繁忙線程數currentThreadsBusy減1,並將線程回收到線程數組中。
1 | /** |
2 | |
3 | * 將線程返還線程池 |
4 | |
5 | */ |
6 | protected synchronized void returnController (ControlRunnable c) { |
7 | |
8 | if(0 == currentThreadCount || stopThePool) { |
9 | |
10 | c.terminate(); |
11 | |
12 | return; |
13 | |
14 | } |
15 | |
16 | // atomic |
17 | |
18 | currentThreadsBusy--; |
19 | |
20 | //將線程回收到pool數組中 |
21 | |
22 | pool[currentThreadCount - currentThreadsBusy - 1] = c; |
23 | |
24 | //notify會喚醒在等待線程資源 |
25 | |
26 | notify(); |
27 | |
28 | } |
配置
在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
1 | /** |
2 | |
3 | * 定期清理空閑線程 |
4 | |
5 | */ |
6 | |
7 | public static class MonitorRunnable implements Runnable { |
8 | |
9 | ThreadPool p; |
10 | |
11 | Thread t; |
12 | |
13 | int interval =WORK_WAIT_TIMEOUT; |
14 | |
15 | boolean shouldTerminate ; |
16 | |
17 | MonitorRunnable(ThreadPool p) { |
18 | |
19 | this.p =p; |
20 | |
21 | this.start(); |
22 | |
23 | } |
24 | |
25 | public void start() { |
26 | |
27 | shouldTerminate = false ; |
28 | |
29 | t = new Thread(this); |
30 | |
31 | t.setDaemon( p.getDaemon() ); |
32 | |
33 | t.setName( p.getName() + "-Monitor"); |
34 | |
35 | t.start(); |
36 | |
37 | } |
38 | |
39 | public void setInterval(int i ) { |
40 | |
41 | this.interval =i; |
42 | |
43 | } |
44 | |
45 | public void run() { |
46 | |
47 | while(true ) { |
48 | |
49 | try { |
50 | |
51 | //Wait一段時間 |
52 | |
53 | synchronized(this ) { |
54 | |
55 | this.wait(interval ); |
56 | |
57 | } |
58 | |
59 | // Check if should terminate. |
60 | |
61 | // termination happens when the pool is shutting down. |
62 | |
63 | if(shouldTerminate ) { |
64 | |
65 | break; |
66 | |
67 | } |
68 | |
69 | //回收空閑線程 |
70 | |
71 | p.checkSpareControllers(); |
72 | |
73 | } catch(Throwable t) { |
74 | |
75 | ThreadPool. log.error("Unexpected exception" , t); |
76 | |
77 | } |
78 | |
79 | } |
80 | |
81 | } |
82 | |
83 | public void stop() { |
84 | |
85 | this.terminate(); |
86 | |
87 | } |
88 | |
89 | /** 停止monitor線程 |
90 | |
91 | */ |
92 | |
93 | public synchronized void terminate() { |
94 | |
95 | shouldTerminate = true ; |
96 | |
97 | this.notify(); |
98 | |
99 | } |
100 | |
101 | } |
ThreadPool.checkSpareControllers方法,用來被TP-Monitor線程調用回收工作線程。
1 | /** |
2 | |
3 | * 被TP-Monitor線程用來回收線程 |
4 | |
5 | */ |
6 | |
7 | protected synchronized void checkSpareControllers() { |
8 | |
9 | if(stopThePool ) { |
10 | |
11 | return; |
12 | |
13 | } |
14 | |
15 | //如果當前空閑線程數大於最大空閑線程數 |
16 | |
17 | if((currentThreadCount - currentThreadsBusy) > maxSpareThreads) { |
18 | |
19 | //回收比最大空閑線程數多出的空閑線程 |
20 | |
21 | int toFree = currentThreadCount - |
22 | |
23 | currentThreadsBusy - |
24 | |
25 | maxSpareThreads; |
26 | |
27 | for(int i = 0 ; i < toFree ; i++) { |
28 | |
29 | ControlRunnable c = pool[currentThreadCount - currentThreadsBusy - 1]; |
30 | |
31 | c.terminate(); |
32 | |
33 | pool[currentThreadCount - currentThreadsBusy - 1] = null; |
34 | |
35 | currentThreadCount --; |
36 | |
37 | } |
38 | |
39 | } |
40 | |
41 | } |
所屬線程池
ThreadPool線程池
ContainerBackgroundProcessor線程
功能
容器后台線程,只有設置backgroundProcessorDelay大於0的容器才會啟動ContainerBackgroundProcessor線程。該線程會調用當前容器的backgroundProcess方法,並且遞歸調用 backgroundProcessorDelay值小於等於0的子容器的方法。
從源碼中看到只有StandardEngine設置了這個backgroundProcessorDelay值為10,所以只有StandardEngine容器啟動ContainerBackgroundProcessor線程, 而其它StandardHost, StandardContext設置的值都是-1。
1 | /** |
2 | |
3 | * 創建一個新的StandardEngine組件,並綁定默認的基礎Valve。 |
4 | |
5 | */ |
6 | |
7 | public StandardEngine() { |
8 | |
9 | super(); |
10 | |
11 | pipeline.setBasic(new StandardEngineValve()); |
12 | |
13 | /* Set the jmvRoute using the system property jvmRoute */ |
14 | |
15 | try { |
16 | |
17 | setJvmRoute(System. getProperty("jvmRoute")); |
18 | |
19 | } catch(Exception ex) { |
20 | |
21 | } |
22 | |
23 | // Engine將擁有reloading線程 |
24 | |
25 | backgroundProcessorDelay = 10; |
26 | |
27 | } |
線程類:ContainerBase.ContainerBackgroundProcessor
1 | /* |
2 | |
3 | * ContainerBase的保護線程類,調用當前容器的backgroundProcess方法,並在一個固定延時后, |
4 | |
5 | * 用它的子容器的backgroundProcess方法 |
6 | |
7 | */ |
8 | |
9 | protected class ContainerBackgroundProcessor implements Runnable { |
10 | |
11 | public void run() { |
12 | |
13 | while (!threadDone ) { |
14 | |
15 | try { |
16 | |
17 | Thread. sleep(backgroundProcessorDelay * 1000L); |
18 | |
19 | } catch (InterruptedException e) { |
20 | |
21 | ; |
22 | |
23 | } |
24 | |
25 | if (!threadDone ) { |
26 | |
27 | //獲得當前容器,作為父容器 |
28 | |
29 | Container parent = (Container) getMappingObject(); |
30 | |
31 | ClassLoader cl = |
32 | |
33 | Thread. currentThread().getContextClassLoader(); |
34 | |
35 | if (parent.getLoader() != null) { |
36 | |
37 | cl = parent.getLoader().getClassLoader(); |
38 | |
39 | } |
40 | |
41 | //處理父容器和所有的子容器 |
42 | |
43 | processChildren(parent, cl); |
44 | |
45 | } |
46 | |
47 | } |
48 | |
49 | } |
50 | |
51 | //處理父容器和所有的子容器 |
52 | |
53 | protected void processChildren(Container container, ClassLoader cl) { |
54 | |
55 | try { |
56 | |
57 | //如果父容器的loader不為null,則將當前線程的上下文類加載器contextClassLoader設置為父容器 |
58 | |
59 | //的loader的類加載器 |
60 | |
61 | if (container.getLoader() != null) { |
62 | |
63 | Thread. currentThread().setContextClassLoader |
64 | |
65 | (container.getLoader().getClassLoader()); |
66 | |
67 | } |
68 | |
69 | //調用父容器的backgroundProcess方法 |
70 | |
71 | container.backgroundProcess(); |
72 | |
73 | } catch (Throwable t) { |
74 | |
75 | log.error("Exception invoking periodic operation: " , t); |
76 | |
77 | } finally { |
78 | |
79 | Thread. currentThread().setContextClassLoader(cl); |
80 | |
81 | } |
82 | |
83 | //獲得父容器的所有子容器 |
84 | |
85 | Container[] children = container.findChildren(); |
86 | |
87 | for (int i = 0; i < children.length; i++) { |
88 | |
89 | //如果子容器的backgroundProcessorDelay小於等於0,則遞歸處理子容器 |
90 | |
91 | if (children[i].getBackgroundProcessorDelay() <= 0) { |
92 | |
93 | processChildren(children[i], cl); |
94 | |
95 | } |
96 | |
97 | } |
98 | |
99 | } |
100 | |
101 | } |
所屬線程池
無