科普:為什麼SpringBoot中main方法執行完畢後程式不會直接退出呢
- 2020 年 2 月 25 日
- 筆記
針對這個問題我們可以轉化一下思路:一個JVM進程,在什麼情況下會正常退出?
大部分人應該都知道使用System.exit()
或Runtime.exit()
可以直接導致當前JVM進程退出,但是仔細想想這個好像跟SpringBoot沒啥關係哈
另外一個可能會導致進程退出的是所有的非daemon進程完全終止,那麼根據這個條件反推的話是不是說只要保證SpringBoot進程中包含1個以上的daemon進程就可以保證程式不會退出
接下來我們去看下SpringBoot是如何基於這個特性實現的
我們以SpringBoot默認使用的Tomcat容器為例,在我之前SpringBoot源碼分析的文章中也提到過,在啟動Tomcat的時候,會調用TomcatWebServer
的initialize
方法,在這個方法中會調用一個startDaemonAwaitThread
方法
private void startDaemonAwaitThread() { Thread awaitThread = new Thread("container-" + containerCounter.get()) { public void run() { TomcatWebServer.this.tomcat.getServer().await(); } }; awaitThread.setContextClassLoader(this.getClass().getClassLoader()); awaitThread.setDaemon(false); awaitThread.start(); }
下面我們在深挖一下,在Tomcat的this.tomcat.getServer().await()
這個方法中,執行緒是如何實現不退出的。這裡為了閱讀方便,去掉了不相關的程式碼。
public void await() { // ... if( port==-1 ) { try { awaitThread = Thread.currentThread(); while(!stopAwait) { try { Thread.sleep( 10000 ); } catch( InterruptedException ex ) { // continue and check the flag } } } finally { awaitThread = null; } return; } // ... }
在await方法中,實際上當前執行緒在一個while循環中每10秒檢查一次 stopAwait這個變數,它是一個volatile類型變數,用於確保被另一個執行緒修改後,當前執行緒能夠立即看到這個變化。如果沒有變化,就會一直處於while循環中。這就是該執行緒不退出的原因,也就是整個spring-boot應用不退出的原因。