科普:為什麼SpringBoot中main方法執行完畢後程式不會直接退出呢

  • 2020 年 2 月 25 日
  • 筆記

針對這個問題我們可以轉化一下思路:一個JVM進程,在什麼情況下會正常退出?

大部分人應該都知道使用System.exit()Runtime.exit()可以直接導致當前JVM進程退出,但是仔細想想這個好像跟SpringBoot沒啥關係哈

另外一個可能會導致進程退出的是所有的非daemon進程完全終止,那麼根據這個條件反推的話是不是說只要保證SpringBoot進程中包含1個以上的daemon進程就可以保證程式不會退出

接下來我們去看下SpringBoot是如何基於這個特性實現的

我們以SpringBoot默認使用的Tomcat容器為例,在我之前SpringBoot源碼分析的文章中也提到過,在啟動Tomcat的時候,會調用TomcatWebServerinitialize方法,在這個方法中會調用一個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應用不退出的原因。