網站首頁 編程語言 正文
項目場景:
在項目批量導入模板數據的時候遇到的一些關于多線程的問題
問題描述
眾所周知在批量導入excel的數據時一般使用阿里巴巴的EasyExcel,內置多線程的處理,但是有一個很大的弊端就是,baseExcel的每一個屬性都是固定的,才可以使用,現在就是導入的模板根據數據庫的字段實時變化,對于easyExcel并不適用(也可能是我對easyExcel使用并不熟練),在網上未找到解決辦法之后,只能自己按照easyExcel的邏輯實現功能,下面是我在過程中遇到的一系列問題,主要是關于多線程和數據庫連接的問題:
##正常使用esayExcel模板:
@ExcelPorperty
private String name;
@ExcelPorperty
private String code;
- 現在導入模板隨數據庫的數據而變化,固不能使用easyExcel導入,下面是我的解決辦法:通過ExcelUtil來parse每一行數據,和數據庫讀取出來的column的map做對照,然后手動處理每一行的數據(這里還是比較簡單的):
List<Map<String,Object>> mapList = ImportExcelUtil.parseExcel(file.getInputStream(),map);
map是數據庫查詢出來的column,file是導入的文件,工具類我放在附件里面
- 現在因為一個模板導入有萬條數據,如果一條一條處理很浪費時間,這里使用多線程來分批次處理,多線程真的很多坑,先分批次切片:
- 下面是主要的分析環節,主要是對數據進行去重,數據庫查詢字段是否匹配,數據庫是否重復,業務邏輯不詳細講解,這里使用了CountDownLatch(CountDownLatch詳解)同步鎖,CountDownLatch允許一個或者多個線程去等待其他線程完成操作。多線程一般和鎖配合使用,這里也是導致我后面遇到了一個問題:
CountDownLatch latch = new CountDownLatch(mapList.size());
主要遇到的問題:
1.
多線程共享靜態變量的問題
學習過多線程的應該了解靜態變量,這邊是原本打算寫一個靜態變量的List<>收集每個線程發生的報錯,后面統一返回,首先在回答共享變量之前,我們應該搞清的是什么是線程安全?
對于線程的安全,通過博客我們可能會得到很多的答案,但在這里我結合一點自己的想法和感受談談線程安全
我想本質的問題就是:啟動線程的方法是start方法,但是真正運行線程的方法是run方法,所以線程安全不安全取決于run方法中的代碼的執行的結果是否一致,如果start啟動的的是多個線程,而run中運行的是多個線程,如果當單個線程運行的結果和多個線程運行的結果不一樣時,那么線程必定是不安全的,反之,如果這多個線程運行的結果和單個線程運行的結果是一致的,那么必然是線程安全的,也就是說,run’中的代碼運行的結果是一致的,所以這個問題就解決了;下面我們回答共享變量的問題;
假如有三個線程:線程一,線程二,線程三,三個線程同時啟動區訪問一個共享變量x,從java虛擬機的角度來說,共享變量存在主內存中.但是變量的修改不能再主內存中修改,因此每一個變量對應的每一個線程都有一個工作內存區域,而這個區域就是每一個變量的修改的區域,也就是說:變量的修改是在工作內存中修改的,而非是在主內存中修改的,為了保證共享變量的可見性,加一個互斥鎖,這個目的是保證某一段時間只能由一個線程修改變量,但是下一個線程讀取變量的值,一定是在主內存中讀取的,其實說白了就是一下三點:
1.主內存-------->修改后的變量,用于讀取
2.工作內存------>共享變量真正修改的地方
3.互斥鎖---------->保證共享變量的安全性;
volidate:
強迫修改后的值,保存在主內存中;
原子性:
sybchonized:具有可見性和原子性,volidate不具有原子性;
但是靜態變量在所有線程共享,假設別的租戶同時使用了這個方法導入模板,很可能會導致數據發生互通,所以這個方案無法實現,這個bug也是在上線之后才發現,后面更改成另一種方案:
使用局部變量,每個線程返回到主線程當中,然后統一收集,但是代碼量增多
2.
多線程上下文不共享的問題
公司使用上下文來獲取當前用戶的公司和租戶信息,但是上下文只在主線程中有效,在線程池(線程池詳解)中沒有上下文,解決辦法比較簡單,手動創造上下文,把值傳入其他線程之中
3.
線程池配置問題
關于項目中的線程配置,多數看項目具體的數據量和cpu數量,這邊項目大概數據1w條,每個線程500條,大概需要20個線程,線程池的優化對效率的作用至關重要,在項目中我因為開始線程池配置的maxPoolSize設置的Integer.MAX,導致數據內存溢出,項目oom,所以線程池的配置至關重要。ThreadPoolExecutor部分源碼
構造方法:
public ThreadPoolExecutor(int corePoolSize, //核心線程數量
int maximumPoolSize,// 最大線程數
long keepAliveTime, // 最大空閑時間
TimeUnit unit, // 時間單位
BlockingQueue<Runnable> workQueue, // 任務隊列
ThreadFactory threadFactory, // 線程工廠
RejectedExecutionHandler handler // 飽和處理機制
)
4.
CountDownLatch 未釋放導致數據庫連接過多
CountDownLatch 是一個適用于當前項目的鎖,這邊設置的size等一線程總數,在每個線程處理完畢之后執行countDownLatch.countDown釋放,主線程中使用countDownLatch.await()阻塞,當所有線程執行完畢之后countDownLatch才釋放,主線程運行,可以有效的處理數據,但是這也導致了一個問題:
其他線程出現了問題:例如數據不匹配,數據庫查詢報錯,非拋出式異常導致線程中斷,并未釋放countDownLatch,主線程一直countDownLatch.await()導致線程一直阻塞,占用數據庫連接,在多次調試之后,占用的數據路連接已經超過數據庫的最大連接數,最后數據庫連接占用過多導致出錯
這個bug十分隱晦所以在開始的時候沒有發現,但是仍然值得重視,解決辦法就是,在線程的try-catch-finally中加入countDownLatch.countDown,無論線程是否報錯都釋放鎖,主線程不阻塞
總結
多線程在使用過程中涉及鎖,靜態變量,上下文,線程間的同步與互斥,線程池配置,需要一定的積累才能使用的好,在很多官方模塊中也使用了線程,以前總是使用現成的輪子,現在造輪子才知道輪子多難造,還是盡量使用官方的代碼塊,所以easyExcel什么時候有根據數據庫實時變動column的功能,我就不用操這么多心了,只能說代碼的迭代需要一代接一代
原文鏈接:https://blog.csdn.net/qq_43171656/article/details/124683092
相關推薦
- 2023-05-08 C++中new和delete匹配使用過程詳解_C 語言
- 2022-08-26 pandas應用實例之pivot函數詳解_python
- 2023-04-19 C++實現反轉鏈表的兩種方法_C 語言
- 2022-10-06 詳解hive常見表結構_數據庫其它
- 2023-03-23 Rust應用調用C語言動態庫的操作方法_Rust語言
- 2023-01-14 Go?庫bytes.Buffer和strings.Builder使用及性能對比_Golang
- 2022-10-16 Python?Flask框架使用介紹_python
- 2022-06-09 ASP.NET?Core中的環境配置_基礎應用
- 最近更新
-
- window11 系統安裝 yarn
- 超詳細win安裝深度學習環境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權
- redisson分布式鎖中waittime的設
- maven:解決release錯誤:Artif
- restTemplate使用總結
- Spring Security之安全異常處理
- MybatisPlus優雅實現加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務發現-Nac
- Spring Security之基于HttpR
- Redis 底層數據結構-簡單動態字符串(SD
- arthas操作spring被代理目標對象命令
- Spring中的單例模式應用詳解
- 聊聊消息隊列,發送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支