SpringBoot Test 多線程報錯:dataSource already closed

1:前言

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

業務代碼大概是這樣的:

@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排除不讓它啓動。

結果:然並卵

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

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

@SpringBootApplication(exclude = {TaskExecutionAutoConfiguration.class})

結果:然並卵

3:關電腦回家喫飯睡覺

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

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

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

3:根本原因

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

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

TimeUnit.SECONDS.sleep(30);

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

4:解決方案

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

ThreadPoolExecutor 的源碼:

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

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

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

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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章