運維反映執行了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是無能為力的。
那么JVM什么時候停止?沒有非deamon的線程在運行就停止了,或者說應用自身的非deamon線程處理完所有事情結束了自己,jvm就停止了。這個現象很好模擬,比如在一個應用里,可以執行的方法里,加上這樣一段代碼:
Thread thread= new Thread(new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000L);
System.out.println(“hello,xiaoxue”);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
thread.setDaemon(false);
thread.start();
然后你再對tomcat進行shutdown,會發現這個進程還在,控制台每隔一秒輸出“hello,xiaoxue”,但是tomcat占用的那些端口已經被釋放了,因為tomcat自己已經退出了,只是應用其的線程還在執行。
所以,解決辦法就是:
當發現無法停止的java進程時,dump出java線程棧,看應用哪些非deamon的線程還在執行,看它的狀態,分析為什么這個線程一直沒有結束。例如我在我本機上這個線程導致java進程不能退出
“Thread-39″ prio=6 tid=0x000000000818e800 nid=0x12010 waiting on condition [0x000000000eb8f000]
java.lang.Thread.State: TIMED_WAITING (sleeping)
at java.lang.Thread.sleep(Native Method)
at com.alibaba.rocketmq.action.TopicAction$1.run(TopicAction.java:48)
at java.lang.Thread.run(Thread.java:662)
這個是上面new出來的,一直死循環,又是個非deamon線程。