tomcat线程初探

博主:handsomecui,希望路过的各位大佬留下你们宝贵的意见,在这里祝大家冬至快乐。

缘由:

    初探缘由,在业务层想要通过(当前线程的栈)来获取到控制层的类名,然后打日志,可是发现并不能通过当前线程获取到控制层的类,两者并没有在一个线程内,进而引发了我对这一问题的思考。

解决步骤:

  一. 我先打印出了当前运行的所有线程的名字以及栈的运行情况:

Thread list size == 21
线程名:main
java.net.PlainSocketImpl:socketAccept
java.net.AbstractPlainSocketImpl:accept
java.net.ServerSocket:implAccept
java.net.ServerSocket:accept
org.apache.catalina.core.StandardServer:await
org.apache.catalina.startup.Catalina:await
org.apache.catalina.startup.Catalina:start
sun.reflect.NativeMethodAccessorImpl:invoke0
sun.reflect.NativeMethodAccessorImpl:invoke
sun.reflect.DelegatingMethodAccessorImpl:invoke
java.lang.reflect.Method:invoke
org.apache.catalina.startup.Bootstrap:start
org.apache.catalina.startup.Bootstrap:main
线程名:ContainerBackgroundProcessor[StandardEngine[Catalina]]
java.io.UnixFileSystem:list
java.io.File:list
org.apache.catalina.startup.HostConfig:deployApps
org.apache.catalina.startup.HostConfig:check
org.apache.catalina.startup.HostConfig:lifecycleEvent
org.apache.catalina.util.LifecycleSupport:fireLifecycleEvent
org.apache.catalina.util.LifecycleBase:fireLifecycleEvent
org.apache.catalina.core.ContainerBase:backgroundProcess
org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor:processChildren
org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor:processChildren
org.apache.catalina.core.ContainerBase$ContainerBackgroundProcessor:run
java.lang.Thread:run
线程名:http-bio-8080-Acceptor-0
java.net.PlainSocketImpl:socketAccept
java.net.AbstractPlainSocketImpl:accept
java.net.ServerSocket:implAccept
java.net.ServerSocket:accept
org.apache.tomcat.util.net.DefaultServerSocketFactory:acceptSocket
org.apache.tomcat.util.net.JIoEndpoint$Acceptor:run
java.lang.Thread:run
线程名:http-bio-8080-AsyncTimeout
java.lang.Thread:sleep
org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout:run
java.lang.Thread:run
线程名:ajp-bio-8009-Acceptor-0
java.net.PlainSocketImpl:socketAccept
java.net.AbstractPlainSocketImpl:accept
java.net.ServerSocket:implAccept
java.net.ServerSocket:accept
org.apache.tomcat.util.net.DefaultServerSocketFactory:acceptSocket
org.apache.tomcat.util.net.JIoEndpoint$Acceptor:run
java.lang.Thread:run
线程名:ajp-bio-8009-AsyncTimeout
java.lang.Thread:sleep
org.apache.tomcat.util.net.JIoEndpoint$AsyncTimeout:run
java.lang.Thread:run
线程名:http-bio-8080-exec-1
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-2
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-3
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-4
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-5
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-6
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-7
java.lang.Thread:getStackTrace
com.thor.dao.domain.common.util.CommonUtil:getThreadGroup
com.thor.service.customer.CustomerService:post
com.thor.service.customer.CustomerService$$FastClassByCGLIB$$9a694ea:invoke
net.sf.cglib.proxy.MethodProxy:invoke
org.springframework.aop.framework.Cglib2AopProxy$CglibMethodInvocation:invokeJoinpoint
org.springframework.aop.framework.ReflectiveMethodInvocation:proceed
org.springframework.transaction.interceptor.TransactionInterceptor:invoke
org.springframework.aop.framework.ReflectiveMethodInvocation:proceed
org.springframework.aop.framework.Cglib2AopProxy$DynamicAdvisedInterceptor:intercept
com.thor.service.customer.CustomerService$$EnhancerByCGLIB$$898851cc:post
com.thor.control.customer.CustomControl:post
sun.reflect.NativeMethodAccessorImpl:invoke0
sun.reflect.NativeMethodAccessorImpl:invoke
sun.reflect.DelegatingMethodAccessorImpl:invoke
java.lang.reflect.Method:invoke
org.springframework.web.method.support.InvocableHandlerMethod:invoke
org.springframework.web.method.support.InvocableHandlerMethod:invokeForRequest
org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod:invokeAndHandle
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter:invokeHandlerMethod
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter:handleInternal
org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter:handle
org.springframework.web.servlet.DispatcherServlet:doDispatch
org.springframework.web.servlet.DispatcherServlet:doService
org.springframework.web.servlet.FrameworkServlet:processRequest
org.springframework.web.servlet.FrameworkServlet:doPost
javax.servlet.http.HttpServlet:service
javax.servlet.http.HttpServlet:service
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
org.apache.tomcat.websocket.server.WsFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
com.thor.control.user.web.filter.AuthFilter:doFilterInternal
com.taovip.login.session.auth.BaseFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
com.thor.control.user.web.filter.LogFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
com.taovip.login.session.client.ClientSessionFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
com.thor.control.user.web.filter.LoginFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
org.springframework.web.filter.CharacterEncodingFilter:doFilterInternal
org.springframework.web.filter.OncePerRequestFilter:doFilter
org.apache.catalina.core.ApplicationFilterChain:internalDoFilter
org.apache.catalina.core.ApplicationFilterChain:doFilter
org.apache.catalina.core.StandardWrapperValve:invoke
org.apache.catalina.core.StandardContextValve:invoke
org.apache.catalina.authenticator.AuthenticatorBase:invoke
org.apache.catalina.core.StandardHostValve:invoke
org.apache.catalina.valves.ErrorReportValve:invoke
org.apache.catalina.valves.AccessLogValve:invoke
org.apache.catalina.core.StandardEngineValve:invoke
org.apache.catalina.connector.CoyoteAdapter:service
org.apache.coyote.http11.AbstractHttp11Processor:process
org.apache.coyote.AbstractProtocol$AbstractConnectionHandler:process
org.apache.tomcat.util.net.JIoEndpoint$SocketProcessor:run
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-8
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-9
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:http-bio-8080-exec-10
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:park
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject:await
java.util.concurrent.LinkedBlockingQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
org.apache.tomcat.util.threads.TaskQueue:take
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
org.apache.tomcat.util.threads.TaskThread$WrappingRunnable:run
java.lang.Thread:run
线程名:mergeAppender
java.lang.Object:wait
java.lang.Object:wait
org.apache.log4j.AsyncAppender$Dispatcher:run
java.lang.Thread:run
线程名:alertAppender
java.lang.Object:wait
java.lang.Object:wait
org.apache.log4j.AsyncAppender$Dispatcher:run
java.lang.Thread:run
线程名:Timer-0
java.lang.Object:wait
java.util.TimerThread:mainLoop
java.util.TimerThread:run
线程名:pool-7-thread-1
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:parkNanos
java.util.concurrent.SynchronousQueue$TransferStack:awaitFulfill
java.util.concurrent.SynchronousQueue$TransferStack:transfer
java.util.concurrent.SynchronousQueue:poll
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
java.lang.Thread:run
线程名:pool-7-thread-2
sun.misc.Unsafe:park
java.util.concurrent.locks.LockSupport:parkNanos
java.util.concurrent.SynchronousQueue$TransferStack:awaitFulfill
java.util.concurrent.SynchronousQueue$TransferStack:transfer
java.util.concurrent.SynchronousQueue:poll
java.util.concurrent.ThreadPoolExecutor:getTask
java.util.concurrent.ThreadPoolExecutor:runWorker
java.util.concurrent.ThreadPoolExecutor$Worker:run
java.lang.Thread:run

代码:

ThreadGroup threadGroup = Thread.currentThread().getThreadGroup();
    int estimatedSize = threadGroup.activeCount() * 2;
    Thread[] slackList = new Thread[estimatedSize];
    // 获取根线程组的所有线程
    int actualSize = threadGroup.enumerate(slackList);
  // copy into a list that is the exact size
    Thread[] list = new Thread[actualSize];
    System.arraycopy(slackList, 0, list, 0, actualSize);
    System.out.println("Thread list size == " + list.length);
    for (Thread thread : list) {
      System.out.println("线程名:" + thread.getName());
      StackTraceElement[] stackTrace = thread.getStackTrace();
      for(StackTraceElement e : stackTrace){
        System.out.println(e.getClassName()+":"+e.getMethodName());
      }
    }

所以看到了这么多线程,网上搜了好多,都看不懂,终于找到了一篇只讲名功能的博客:https://www.cnblogs.com/dhcn/p/7120713.html

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) + "-";
   }
 
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对象。

protected class Worker implements Runnable {
 
    protected Thread thread = null;
 
    protected boolean available = false;
 
    protected Socket socket = null;
 
    /**
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空间。

任务分配时序图

1

任务分配

通过JIoEndPoint中createWorkerThread方法获得一个工作线程。如在工作线程栈workers中获得一个线程对象,如果线程栈已经是空的,并且当前线程数量curThreads还小于最大线程数maxThreads,那么就创建一个新的工作线程。然后调用Work.assign方法分配给工作线程。

protected Worker createWorkerThread() {
 
    //获得工作线程栈workers的锁
 
    synchronized (workers ) {
 
        //如果工作线程栈里有线程则返回栈顶工作线程
 
        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方法。

protected void recycleWorkerThread(Worker workerThread) {
 
    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线程。

public void start() throws Exception {
 
    // Initialize socket if not done before
 
    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 = 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线程。

public synchronized void start() {
 
    stopThePool=false ;
 
    currentThreadCount  = 0;
 
    currentThreadsBusy  = 0;
 
    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。

/**
 
 * Executes a given Runnable on a thread in the pool, block if needed.
 
 */
 
public void runIt(ThreadPoolRunnable r) {
 
    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,并将线程回收到线程数组中。

/**
 
 * 将线程返还线程池
 
 */
protected synchronized void returnController (ControlRunnable c) {
 
    if(0 == currentThreadCount || stopThePool) {
 
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

/**
 
 * 定期清理空闲线程
 
*/
 
public static class MonitorRunnable implements Runnable {
 
    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线程调用回收工作线程。

/**
 
 * 被TP-Monitor线程用来回收线程
 
 */
 
protected synchronized void checkSpareControllers() {
 
    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。

/**
 
 * 创建一个新的StandardEngine组件,并绑定默认的基础Valve。
 
 */
 
public StandardEngine() {
 
    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

 
 
/*  
  
 * ContainerBase的保护线程类,调用当前容器的backgroundProcess方法,并在一个固定延时后,  
  
 * 用它的子容器的backgroundProcess方法  
  
 */  
 
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 }

所属线程池

 

排查结果:

   仔细看了栈,发现就是可以找到那个类名,就是一个线程。控制层和业务层怎么会不是一个线程。。。错误原因是我限制了for循环的个数,所以没找到。。。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值