日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學無先后,達者為師

網站首頁 編程語言 正文

批量導入模板數據的時候遇到的一些關于多線程的問題

作者:_武漢彭于晏_ 更新時間: 2022-05-11 編程語言

項目場景:

在項目批量導入模板數據的時候遇到的一些關于多線程的問題


問題描述

眾所周知在批量導入excel的數據時一般使用阿里巴巴的EasyExcel,內置多線程的處理,但是有一個很大的弊端就是,baseExcel的每一個屬性都是固定的,才可以使用,現在就是導入的模板根據數據庫的字段實時變化,對于easyExcel并不適用(也可能是我對easyExcel使用并不熟練),在網上未找到解決辦法之后,只能自己按照easyExcel的邏輯實現功能,下面是我在過程中遇到的一系列問題,主要是關于多線程和數據庫連接的問題:

##正常使用esayExcel模板:
@ExcelPorperty
private String name;

@ExcelPorperty
private String code;
  1. 現在導入模板隨數據庫的數據而變化,固不能使用easyExcel導入,下面是我的解決辦法:通過ExcelUtil來parse每一行數據,和數據庫讀取出來的column的map做對照,然后手動處理每一行的數據(這里還是比較簡單的):
List<Map<String,Object>> mapList = ImportExcelUtil.parseExcel(file.getInputStream(),map);

map是數據庫查詢出來的column,file是導入的文件,工具類我放在附件里面

  1. 現在因為一個模板導入有萬條數據,如果一條一條處理很浪費時間,這里使用多線程來分批次處理,多線程真的很多坑,先分批次切片:
    在這里插入圖片描述
  2. 下面是主要的分析環節,主要是對數據進行去重,數據庫查詢字段是否匹配,數據庫是否重復,業務邏輯不詳細講解,這里使用了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

欄目分類
最近更新