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 | } |
所屬線程池
無


