tomcat为什么无法关闭

执行了tomcat的shutdown脚本后,java进程仍然存在,认为tomcat的关闭脚本不可靠。好吧,其实shutdown.sh无法停止,不是tomcat问题,是应用有问题,否则可以跑一个空tomcat,保证shutdown百分之百生效。

先说一下tomcat的大概关闭过程:
1 停止连接处理线程Accepter,停止接受新的请求
2 关闭tomcat自身的资源,例如各种service,连接器,protocol,container
3 然后tomcat主线程结束了,就是执行BootStrap这个类的线程

这是一个平滑关闭的过程,但是什么时候会导致所谓”tomcat无法关闭”?要更正一点,不是tomcat无法关闭,是执行tomcat的这个jvm无法关闭,这是本质的不同。

最根本的原因是:当前运行tomcat的jvm里还有非deamon的线程没有结束执行,例如被阻塞,或者还在执行任务。这个现象就是tomcat 端口都已经关闭了,但是java进程还在。tomcat的停止只是结束了自己的线程,关闭了自己的资源。但是应用开启的非deamon线程,这个 tomcat是无能为力的。

netstat -ano|grep 8083 /*可以通过此确认tomcat是否已关闭*/

那么JVM什么时候停止?没有非deamon的线程在运行就停止了,或者说应用自身的非deamon线程处理完所有事情结束了自己,jvm就停止了。

那么当发现tomcat停止了,但是ps -ef|grep tomcat进程依然在,如何查看应用那个地方线程是非deamon的呢?

查看办法很简单,执行如下命令:

$JAVA_HOME/bin/jstack  
例:

[root@localhost bin]# ps -ef|grep tomcat  
root      1513     1  2 23:41 pts/1    00:00:01 /usr/local/share/java/jdk1.6.0_25/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-6.0.32/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/opt/apache-tomcat-6.0.32/endorsed -classpath /opt/apache-tomcat-6.0.32/bin/bootstrap.jar -Dcatalina.base=/opt/apache-tomcat-6.0.32 -Dcatalina.home=/opt/apache-tomcat-6.0.32 -Djava.io.tmpdir=/opt/apache-tomcat-6.0.32/temp org.apache.catalina.startup.Bootstrap start  
root      1544  1462  0 23:42 pts/1    00:00:00 grep --color=auto tomcat  
可以看到tomcat进展pid为1513,则下面定位到jdk/bin目录,执行jstack1513 查看 工程里到底哪里开启的新的非守护线程?

$ cd /home/software/jdk1.7.0_79/bin
$ jstack 1513

调用jstack查看:

[root@localhost bin]# jstack 1513       
2011-07-12 23:44:00
Full thread dump Java HotSpot(TM) Client VM (20.0-b11 mixed mode, sharing):

"Attach Listener" daemon prio=10 tid=0xb41d7c00 nid=0x606 waiting on condition [0x00000000]
   java.lang.Thread.State: RUNNABLE

"TP-Monitor" daemon prio=10 tid=0xb41d6400 nid=0x5fa in Object.wait() [0xb3e0b000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
        at java.lang.Object.wait(Native Method)
        - waiting on <0x87143720> (a org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable)
        at org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable.run(ThreadPool.java:565)
        - locked <0x87143720> (a org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable)
        at java.lang.Thread.run(Thread.java:662)

"TP-Processor4" daemon prio=10 tid=0xb41d4c00 nid=0x5f9 runnable [0xb3e5c000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
        - locked <0x87124770> (a java.net.SocksSocketImpl)
        at java.net.ServerSocket.implAccept(ServerSocket.java:462)
        at java.net.ServerSocket.accept(ServerSocket.java:430)
        at org.apache.jk.common.ChannelSocket.accept(ChannelSocket.java:311)
        at org.apache.jk.common.ChannelSocket.acceptConnections(ChannelSocket.java:668)
        at org.apache.jk.common.ChannelSocket$SocketAcceptor.runIt(ChannelSocket.java:879)
        at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
        at java.lang.Thread.run(Thread.java:662)

其中看到"Attach Listener" daemon prio=10 tid=0xb41d7c00 nid=0x606 waiting on condition [0x00000000]

   java.lang.Thread.State: RUNNABLE

这行,最前变的"Attach Listener" 是线程名, 紧跟其后的 daemon是线程的守护状态,

 

其中主线程不是daemon的,所以是这样:

"main" prio=10 tid=0xb6d05000 nid=0x5ea runnable [0xb6ee9000]
   java.lang.Thread.State: RUNNABLE
        at java.net.PlainSocketImpl.socketAccept(Native Method)
        at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
        - locked <0x871644a8> (a java.net.SocksSocketImpl)
        at java.net.ServerSocket.implAccept(ServerSocket.java:462)
        at java.net.ServerSocket.accept(ServerSocket.java:430)
        at org.apache.catalina.core.StandardServer.await(StandardServer.java:431)
        at org.apache.catalina.startup.Catalina.await(Catalina.java:676)
        at org.apache.catalina.startup.Catalina.start(Catalina.java:628)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
        at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)

在"main" 后没有daemon,看到这样的线程状态,顺藤摸瓜,找到对应new Thread的地方setDaemon(true)就可以痛痛快快的关闭tomcat,停止应用了。


你可能感兴趣的:(Tomcat)