1. 背景
在 jstack 的內容中可以看到以下的 MySQL Statement Cancellation Timer 守護線程, 在業務高峰期的時候會出現大量的這類守護線程, 由此追溯該線程的生命周期過程;
"MySQL Statement Cancellation Timer" #20647 daemon prio=5 os_prio=0 tid=0x00007f2d087e9800 nid=0xfb83 in Object.wait() [0x00007f2b4b45a000]
   java.lang.Thread.State: TIMED_WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	at java.util.TimerThread.mainLoop(Timer.java:552)
	- locked <0x00000005da147038> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)
   Locked ownable synchronizers:
	- None
"MySQL Statement Cancellation Timer" #24138 daemon prio=5 os_prio=0 tid=0x00007f402802c800 nid=0x4cf64 in Object.wait() [0x00007f3e49453000]
   java.lang.Thread.State: WAITING (on object monitor)
	at java.lang.Object.wait(Native Method)
	at java.lang.Object.wait(Object.java:502)
	at java.util.TimerThread.mainLoop(Timer.java:526)
	- locked <0x00000005f606cc60> (a java.util.TaskQueue)
	at java.util.TimerThread.run(Timer.java:505)
   Locked ownable synchronizers:
	- None
 
         
         
        2. TimerThread
java.util.TimerThread 是 Timer.java 文件里的一個內部類, 主要負責 Timer 隊列任務的執行和調度;

- 根據定位 
Timer.java:526位置的代碼, 當前狀態WAITING (on object monitor), 表示當前的timer線程池為空, 正在等待新入駐; - 根據定位 
Timer.java:552位置的代碼, 當前狀態TIMED_WAITING (on object monitor)表示任務等待被激活; 

3. getCancelTimer
根據線程名稱 MySQL Statement Cancellation Timer 繼續追溯, 在 com.mysql.jdbc.ConnectionImpl#getCancelTimer 方法中找到該 TimerThread 的創建(cancelTimer):

4. getCancelTimer 的上游調用
主要是 mysql-connector-java-xxx.jar 中負責 sql 查詢的 Statement


5. 創建 CancelTask timeoutTask
在 com.mysql.jdbc.StatementImpl#executeQuery 方法中可以發現, 當啟用 queryTimeout 且 timeoutInMillis!=0 時, 在執行 sql 的時候就會創建一個 CancelTask 的線程來控制超時; (后面那個 versionMeetsMinimum 是個版本判斷可以先忽略)

然后在項目的 application.yml 中發現配置 mybatis.configuration.default-statement-timeout: 5, 所以 mybatis 在每次的數據庫查詢都會加上 queryTimeout, 且該配置對全局 SQL 生效, 包括 insert, select, update;

6. CancelTask 執行過程
在 com.mysql.jdbc.StatementImpl.CancelTask#run 方法中, 會另起一個線程, 判斷如果啟用了 queryTimeoutKillsConnection 的配置時, 會調用當前 Statement 對應的 Connection 里的 realClose 方法;

在 realClose 方法里發現會關閉 cancelTimer 線程;

7. Connection 關閉時
在 com.mysql.jdbc.ConnectionImpl#close 方法里也會發現有 realClose 方法的調用, 即在連接關閉時也會處理 cancelTimer 的釋放

8. 總結 MySQL Statement Cancellation Timer 線程的流程

設置了 queryTimeout 會使 jdbc driver 在每次查詢數據庫時新建 CancelTask(timeoutTask對象) 線程來處理超時, 並使用 CancelTimer(在 ConnectionImpl類中) 來進行調度;
 如果 SQL 查詢超時了, 則會在 timeoutTask 的 run 方法里調用 com.mysql.jdbc.ConnectionImpl#realClose 來釋放 CancelTimer;
 如果 Connection 正常關閉 close 時, 也會調用 com.mysql.jdbc.ConnectionImpl#realClose 來釋放 CancelTimer;
