tomcat nio并不是真正的异步io,其实是io复用,可以说是非阻塞的,但不是真正的异步。
tomcat的NioEndpoint启动的ServerSocket是阻塞的,Acceptor线程里边阻塞从accept()获取socket
socket是非阻塞的,每个socket的channel注册到一个Poller线程上去,Poller会把这个channel包装成一个PollerEvent,并放到events队列里去。
#默认有两个Poller线程,每个Poller线程里边有一个Selector,以及一个队列:SynchronizedQueue<PollerEvent> events
btw:Acceptor线程和Poller线程都是NioEndpoint的内部类实现的)
Poller线程内部不断循环events队列 1、从里边拿出PollerEvent去执行它的run方法,其中将PollerEvent的SocketChannel注册到该poller的Selector中。
2、SocketChannel如果readable则执行processKey()
processKey()里边调用processSocket(),processSocket()里边把socket包装成SocketProcessor交给线程池去执行。
#线程池
在NioEndpoint的startInternal()方法里,也就是在启动Poller和Acceptor线程之前把线程池建立起来createExecutor()
//创建线程池
public void createExecutor() {
internalExecutor = true;
TaskQueue taskqueue = new TaskQueue(); //任务队列
TaskThreadFactory tf = new TaskThreadFactory(getName() + "-exec-", daemon, getThreadPriority());//工程模式,告诉线程池怎么创建里边的线程
//参数:核心线程数、最大线程数、60秒keepalive、任务队列、以及用来创建线程用的线程工厂
executor = new ThreadPoolExecutor(getMinSpareThreads(), getMaxThreads(), 60, TimeUnit.SECONDS,taskqueue, tf);
taskqueue.setParent( (ThreadPoolExecutor) executor);//任务队列里边存了一下线程池的引用,以后会用到
}
接下来就是研究TaskQueue、ThreadPoolExecutor了
看过来其实tomcat线程池还是基于jdk线程池的,只是在初始化的时候传入了自定义的TaskQueue和ThreadFactory, 从而达到自定义线程池的目的。
最终目的是要分析好两份jstact样本:
异常,将近400个处于WAITING
"http-nio-8081-exec-385" #459 daemon prio=5 os_prio=0 tid=0x00007f4b6819a800 nid=0x674e waiting on condition [0x00007f4b2e977000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000006c8ddb088> (a java.util.concurrent.locks.ReentrantLock$NonfairSync)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2083)
at java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:85)
at org.apache.tomcat.util.threads.TaskQueue.poll(TaskQueue.java:31)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1073)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
正常样本: 无请求时,tomcat线程池中idle线程10个,等待处理请求
"http-nio-8081-exec-1" #17 daemon prio=5 os_prio=0 tid=0x00007f3bf8aa9800 nid=0x15d0 waiting on condition [0x00007f3bd54d4000]
java.lang.Thread.State: WAITING (parking)
at sun.misc.Unsafe.park(Native Method)
- parking to wait for <0x00000000e41b3ad8> (a java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject)
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at java.util.concurrent.LinkedBlockingQueue.take(LinkedBlockingQueue.java:442)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:103)
at org.apache.tomcat.util.threads.TaskQueue.take(TaskQueue.java:31)
at java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1067)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1127)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
to be continuted ...