SpringBoot Test 多線程報錯:dataSource already closed


1:背景

最近在項目中使用多線程對大任務拆分處理時,進行數據庫操作的時候報錯了。
drawing

業務代碼大概是這樣的:

@Service
public calss TestServiceImpl implements TestService{
	@Resource
	private TestMapper testMapper;

	public void insert(Test test){
		ThreadPoolExecutor executor = new ThreadPoolExecutor(...);

		executor.execute(() -> {
			testMapper.insert(test);
		});
	}
}

測試用例代碼:

@RunWith(SpirngRunner.class)
@SpringBootTest
public class ApplicationTests{

	@Resource
	private TestService testService;

	@Test
	public void insertTest(){
		Test test = new Test();
		testService.insert(test);
	}

}

2:排查思路

1:項目中使用了一個很老舊的定時器工具(LTS),由於配置未接入,啟動時LTS一直會報錯的,

我首先懷疑是LTS的問題,是不是內部某部分源碼調用了DruidDataSource的close方法。
因此我在項目中先把LTS排除不讓它啟動。
drawing

結果:然並卵

2:后來懷疑是不是項目中的定時任務造成的,因為數據源關閉前打印了以下日志

drawing

可以看到 Thread-2 執行了ThreadPoolTaskExecutor 再關閉數據源的。
因此我在SpringBoot 的啟動類上加上了以下代碼

@SpringBootApplication(exclude = {TaskExecutionAutoConfiguration.class})

結果:然並卵

3:關電腦回家吃飯睡覺

第二天早上回到工位上緩了一會,思考還有什么原因會導致這種問題。
因為我們的線程池是一個封裝的工具類封裝過的(我封裝的),里面有兩行代碼引起了我的注意:

drawing

我先把 threadPoolExecutor.awaitTermination(3, TimeUnit.MINUTES); 這行代碼注釋掉,重新跑測試用例。

drawing

發現並沒有報錯,並且程序很快就終止運行了。
在這里我就想到了,SpringBoot Test 中主線程退出,是不會管用戶線程是否結束了任務的這個問題。

3:根本原因

1:SpringBoot Test 主線程退出,導致Spring 容器關閉。
2:Spring容器關閉,導致DruidDataSource 關閉
3:此時用戶線程去訪問已關閉的數據源,導致報錯。

drawing

這樣不行啊,那我測試用例怎么跑完呢?
可能這個時候有人會想到在測試用例最后面加以下代碼:

TimeUnit.SECONDS.sleep(30);

可是本人覺得不夠優雅,也不靈活。

4:解決方案

監聽容器關閉,關閉前先判斷線程池中是否存在未執行完任務的線程。

ThreadPoolExecutor 的源碼:
drawing
因此我們是可以知道是否存在未執行完任務的線程的。

所以我在項目中寫了以下代碼:
drawing

當線程池中存在未執行完任務的線程的時候,就先等待,直到所有任務完成或超過等待時間。

可把自己牛逼壞了(叉會腰)!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM