999精品在线视频,手机成人午夜在线视频,久久不卡国产精品无码,中日无码在线观看,成人av手机在线观看,日韩精品亚洲一区中文字幕,亚洲av无码人妻,四虎国产在线观看 ?

POI用戶模型的重構與優化

2019-10-08 06:43:30吉豪杰宋欣潮
軟件 2019年5期

吉豪杰 宋欣潮

摘 ?要: Apache POI中的用戶模型是目前用于處理Excel數據的最為廣泛的應用技術,但用戶模型存在許多明顯的弊端。本文將以一個學生檔案管理系統為例,對用戶模型中存在的問題以及產生問題的原因進行分析,并針對這些問題,借用面向過程的設計思想對用戶模型進行優化和改進。使用不同規模的數據量對改進前后的用戶模型進行測試,并對測試結果進行比較和分析,最終在一定程度上解決了用戶模型的弊端,提升了用戶模型的性能。

關鍵詞: 用戶模型;生產者消費者模型;POI技術;數據優化;內存溢出;設計模式;java多線程

中圖分類號: TP315 ? ?文獻標識碼: A ? ?DOI:10.3969/j.issn.1003-6970.2019.05.038

本文著錄格式:吉豪杰,宋欣潮. POI用戶模型的重構與優化[J]. 軟件,2019,40(5):193199

【Abstract】: The UserModel in Apache POI is the most widely used technology for processing Excel data at present, but the user model has many obvious disadvantages.This paper will take a student file management system as an example to analyze the problems existing in the UserModel and the causes of the problems. In view of these problems, the UserModel will be optimized and improved by using the process-oriented design idea.The data volumes of different scales were used to test the UserModel before and after the improvement, and the test results were compared and analyzed. Finally, the disadvantages of the UserModel were solved to some extent, and the performance of the UserModel was improved.

【Key words】: UserModel; Producer consumer model; POI; Data optimization; Out of memoryerror; Design mode; Java multithreading

0 ?引言

在當前的軟件開發中,越來越多的需求涉及到對MicroSoft Office文檔的處理,其中對Excel數據文檔的處理尤為普遍。因此,關于對Excel文件處理的討論與研究也愈演愈烈。到目前為止,已經出現了許多關于處理Excel文件的技術和開源項目,例如Java Excel Api(jxl),Apache POI[1],Alibaba EasyExcel等。這些開源項目各有特色,都能夠適用于不同的開發場景,滿足了大多數不同的開發需求,但也存在諸多問題,其中對Apache POI相關技術所存在問題的研究是本文討論的重點。本文的創新點在于將面向過程的生產者消費者模型[2]的設計思想應用到傳統的用戶模型當中,以及采用多線程[3-9]的方式實現用戶模型,對傳統的用戶模型處理Excel數據的程序進行重構,使用戶模型在程序結構上邏輯更加清晰、功能更加明確,在數據處理能力上更加高效。

1 ?POI用戶模型

1.1 ?用戶模型簡介

POI是由Apache組織提供的用java編寫的免費開源的跨平臺的 Java API,Apache POI提供API給Java程序對MicroSoft office格式檔案讀和寫的功 能[1]。POI中關于Excel數據處理的部分主要包括User API、Event API和Streaming UserModel API。本文將重點討論User API中的UserModel。

所謂的UserModel實際上就是基于Dom方式的解析,Dom解析就是將文件全部讀入內存,對文件內部的結構進行建模,形成一顆Dom樹的過程,如圖1用戶模型的Dom樹結構。

從圖1中可以看出,用戶模型提供封裝好的Workbook、Sheet、Row、Cell等實例來完成對excel數據的讀寫。

1.2 ?用戶模型的應用

經過對用戶模型的簡單介紹,現在以文獻[4]中提到的學生檔案管理系統為例,對用戶模型的Excel數據解析功能進行實現。該功能的業務處理過程為:①以流的方式接受excel文件;②根據接受的文件生成WorkBook對象;③根據Dom結構,遍歷每個Sheet的每一個Row,將每一個Row中的Cell的值讀取出來,存放到list集合中;④對list中的數據進行類型轉換,并封裝到領域對象Student中,生成存放Student對象的集合;⑤利用數據庫的批量添加操作,將Student集合持久化到數據庫中(涉及到多表操作)。

用戶模型代碼:

Student類:

public class Student {

private BigInteger stuId;//學號

private Archive archive;//檔案

private Profession pro;//專業

private Department department;//院系

private String stuName;//姓名

private String stuSex;//性別

private String stuSendnum;//派遣證號

private String stuClass;//班級

private String stuLocation;//生源地

//省略部分屬性和set、get方法

}

用戶模型解析excel方法:

public static List> getListByExcel(InputStream in,String fileName) throws Exception{

List> list=null;

Workbook wb=getWorkBook(in,fileName); //獲取WorkBook對象

if (wb!=null){

Sheet sheet=null;

Row row=null;

Cell cell=null;

list=new ArrayList>();

for (int i=0;i

sheet=wb.getSheetAt(i);

if (sheet==null){continue;}

//遍歷每一行

for (int j=sheet.getFirstRowNum(); j<=sheet.getLastRowNum();j++){

row=sheet.getRow(j);

Integer columns= (int)row. getLastCellNum();

//遍歷每一列

List rowData=new ArrayList<>();

for (int m=0;m

cell=row.getCell(m);

if (cell!=null) {

rowData.add (getCellValue(cell));//數據類型轉換

}else{

rowData.add("");

}

}

//將每一行的數據放到list中

list.add(rowData);

}

}

}

return list;

}

業務邏輯方法:

public boolean uploadExcel(InputStream in, MultipartFile file) {

//獲取數據源

List> rowList=getListByExcel(in,file.getName());

//數據源封裝成業務對象Student,存放到studentList中

//調用dao層的數據庫操作方法,批量添加Student對象

//添加成功返回true,否則返回false

}

1.3 ?用戶模型的缺陷及原因分析

采用用戶模型的方式讀寫Excel文件,由于這種方式容易理解,操作也較簡單,所以成為了開發人員最常用的方式。但是這種方式也存在很明顯的弊端:因為該方式是基于Dom內存解析的,所以對系統內存的依賴也就比較嚴重。也就是說,WorkBook對象是將整個文件流緩沖到系統內存當中,使用用戶模型處理數據就要求系統有足夠的內存空間去容納以WorkBook對象為根的Dom結構。

在1.2的代碼中可以看出,系統不僅會為WorkBook對象分配內存,而且還會為封轉的Student對象集合分配大量空間。當數據量逐漸增大,對系統內存和cpu的開銷也隨之增大,直到系統沒有足夠的內存去處理數據時,就會造成內存溢出,導致數據處理失敗。

以學生檔案管理系統為例,對用戶模型的系統資源使用情況進行分析。如圖2數據量為1w時系統內存使用情況。

從圖2中有上下兩條曲線,上方曲線表示堆空間的最大值,下方曲線表示當前程序已經占用的空間。當有1萬條excel數據時,系統會在一定時間內對這些數據分配內存并處理,占用內存大小大約60M,處理完數據后,立即釋放空間。對于1萬數據所占用的內存,系統完全有能力分配,所以不會造成內存溢出的情況。

如圖3數據量為10w時系統內存使用情況。

由圖3可知,當數據量增加到10萬時,系統內存占用空間從100M迅速增加到700M以上并且呈緩慢上升的趨勢。系統的堆空間也逐漸擴容到最大值,無法繼續擴大,系統的可用內存資源迅速減少,直到系統沒有足夠的內存空間時,拋出內存溢出異常,此時10w條數據添加到數據庫失敗。

內存占用空間迅速增加是因為生成了基于Dom結構的WorkBook對象,呈緩慢上升趨勢是因為系統在不斷生成Student對象。 正是因為WorkBook對象和不斷生成的Student對象,才造成了系統內存溢出。

雖然目前已經有很多方法可以替代用戶模型去完成大數據量的Excel數據解析功能,比如POI中的基于事件驅動的事件模型和基于流式解析的流用戶模型、基于xml的excel數據解析方式[7]等,但是對于用戶模型本身的缺陷還未能夠解決。解決用戶模型的內存溢出問題,主要從兩方面入手:一是減少WorkBook對象占用的內存空間,另一個是優化不斷生成的Student對象的存儲結構,本文將重點從后者入手來解決內存溢出問題。

2 ?生產者消費者模型

2.1 ?介紹

生產者消費者模型[2]是面向過程編程中的一種高效設計模式[8],該模型包括三種角色:生產者、消費者、數據倉庫。其中生產者負責獲取數據、數據類型轉換等數據準備過程,消費者負責處理生產者準備好的數據,該模型通過一個數據倉庫來實現生產者與消費者的解耦,降低生產者與消費者之間的依賴關系,并且支持并發、忙閑不均等情況的處理。如圖4生產者消費者模型的工作原理。

2.2 ?應用

通常,處理Excel數據的過程為:①通過文件流來獲取數據源,②根據具體的業務邏輯對數據源中的數據進行封裝,③對封裝好的數據進行相應的業務處理。這樣的數據處理過程與生產者消費者模型的工作原理非常相似,過程①和②可以通過生產者來完成,數據存儲可以通過數據倉庫來完成,過程③可以用消費者來實現。

數據倉庫通過維持一個恒定大小的緩沖區域來保證系統的內存消耗,可以有效解決內存溢出問題。由于生產者消費者模型支持并發,所以可以利用多線程的方式來高效率實現數據解析過程,充分利用cpu資源,達到提升系統性能的目的。通過對生產者消費者模型的利用,讓數據解析過程的邏輯更加清晰,各部分分工明確,便于后期的系統維護和更改。

3 ?重構用戶模型

3.1 ?利用生產者消費者模型改進用戶模型

通過1.2對用戶模型的實現和1.3用戶模型的缺陷中可知,用戶模型雖然易于理解,抽象層次高,但對系統性能的開銷非常大,并且1.2中的業務邏輯方法既要獲取數據又要執行數據庫操作,不符合高內聚低耦合的程序設計原則。為解決用戶模型對系統資源的依賴,且使得代碼充分解耦,利用生產者消費者對用戶模型進行改進:

首先將業務邏輯的處理分為數據準備、數據存儲、數據持久化三個過程;

生產者負責數據準備過程,主要完成獲取數據,數據封裝操作;數據倉庫負責臨時存儲數據;消費者負責數據持久化過程;

生產者和消費者以多線程的方式對數據進行并發處理。

對用戶模型的改進,這里主要使用兩種方式來實現:一種是單生產者-單消費者方式,一種是多生產者-多消費者方式。其中單生產者-單消費者方式在數據準備過程中,對于所有的數據都通過一個生產者來負責,雖然通過數據倉庫可以保證系統中產生的Student對象不會占用過多的內存空間,但這樣的執行效率顯然不高。所以使用多生產者方式來實現,即多個生產者并行生產數據,比如10w條數據,若使用10個生產者,那么每個生產者平均負責生產1W條數據,忽略線程等待的時間,10w條數據生產完的時間相當于10個生產者都生產完1w條數據的時間。也就是說,將原來一個生產者生產10w條數據的任務交個10個生產者去完成,這樣的執行效率會有顯著提升。如圖5改進后的用戶模型業務處理流程。

實現代碼如下:

數據倉庫:

public class DataWarehouse {

private static int ?MAX_SIZE=5;//倉庫大小

public static boolean isFinish=false;//生產過程是否結束

private BlockingQueue> dw=new LinkedBlockingQueue<>();//緩沖區

//生產者生產數據

public synchronized void addDatas(List datas){

while(dw.size()>=MAX_SIZE){

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

dw.add(datas);

this.notifyAll();//喚醒所有等待的線程,主要喚醒消費者,防止死鎖

}

//消費者消費數據

public synchronized List consumeDatas(){

while(dw.size()<=0){

if(isFinish){//表示生產過程結束,且緩沖區為空,此時結束消費過程

this.notifyAll();

return null;

}

try {

this.wait();

} catch (InterruptedException e) {

e.printStackTrace();

}

}

List list=dw.poll();

this.notifyAll();//喚醒所有在等待的線程,主要喚醒生產者

return list;

}

public BlockingQueue> getData(){

return dw;

}

}

生產者(單生產者方式):

public class Producter extends ?Thread{

private DataWarehouse dataWarehouse;//數據倉庫

private InputStream inputStream;//輸入流

private String fileName;//文件名

private TransDataUtil transDataUtil;//數據封裝類,將excel數據封裝成業務對象

public void run() {

//遍歷數據源,獲取數據單元,數據單元的大小不宜過大也不宜過小,過大會占用內存空間,過小會增加與數據庫交互的次數和頻率,根據實驗和研究,這里將單元大小設置成1000

if(list.size()>=1000){

dataWarehouse.addDatas(new ArrayList<>(list));//通過數據倉庫生產數據

list.clear();

}

DataWarehouse.isFinish=true;//表示生產數據結束

}

}

生產者(多生產者方式):

public class Producter extends ?Thread{

private DataWarehouse dataWarehouse;

private Sheet sheet;

private int start;//開始行

private int size;//數據源大小

private TransDataUtil transDataUtil;

public void run() {

Row row=null;

Cell cell=null;

List list=new ArrayList<>();

for (int i=this.start;i

row=sheet.getRow(i);

Integer columns= (int)row.getLastCellNum();

List rowData=new ArrayList<>();

for (int m=0;m

cell=row.getCell(m);

if (cell!=null) {

rowData.add(ExcelUtil. getCellValue(cell));

}else{

rowData.add("");

}

}

//將rowData轉化成對象Student放到list中

Student t=transDataUtil.transData(rowData);

list.add(t);

if(list.size()>=1000){//設置一個數據單元大小為1000

dataWarehouse.addDatas(new ArrayList<>(list));

list.clear();

}

}

if (list.size()>0) {

dataWarehouse.addDatas(list);

}

list=null;

DataWarehouse.isFinish=true;

}

}

消費者:

public class Consumer extends ?Thread{

private DataWarehouse dataWarehouse;

private StudentService studentService;//業務邏輯類,負責業務邏輯的處理

public void run() {

while(true){

List studentList=dataWare-house.consumeDatas();//通過數據倉庫取出數據

if (studentList==null) {

break;

}else if (studentList.size()>0) {

studentService.addStudentBatch(studentList);

studentList=null;

System.gc();

}

}

}

}

業務邏輯方法:(單生產者-單消費者):

public boolean uploadExcelByPC(InputStream in, MultipartFile file){

DataWarehouse.isFinish=false;

producter.setFileName(file.getOriginalFilename());

producter.setInputStream(in);

new Thread(producter,"producter").start();

new Thread(consumer,"consumer").start();

return true;

}

業務邏輯方法:(多生產者-多消費者):

public boolean uploadExcelByPC(InputStream in, MultipartFile file){

DataWarehouse.isFinish=false;

List threadList=new ArrayList<>();

try {

Workbook wb = ExcelUtil.getWorkBook(in, file.getOriginalFilename());

if (wb!=null) {

Sheet sheet = null;

Row row = null;

Cell cell = null;

for (int i=0;i

sheet=wb.getSheetAt(i);

if (sheet==null){ continue;}

producter.setSheet(sheet);

int size=sheet.getLastRowNum();

int pSize=5000;//表示一個生產者負責生產5000條數據

int start=0;

int index=0;

while(size>=pSize && size>0){

producter.setSize(pSize);

producter.setStart(start);

start+=pSize;

size-=pSize;

threadList.add(new Thread(producter,"producter-"+index));

index++;

}

if (size>0){

producter2.setSize(size);

producter2.setStart(start);

threadList.add(new Thread(producter,"producter-"+index));

}

}

}

for (Thread t:threadList) {t.start();}

new Thread(consumer,"consumer1").start();

new Thread(consumer,"consumer2").start();

} catch (Exception e) {

e.printStackTrace();

}

return true;

}

3.2 ?性能分析

執行3.1中的實現代碼,并監控jvm的內存使用情況,如下圖為改進后的用戶模型處理不同數據量時系統內存的使用情況。

從圖7中可以看出,系統將文件流緩沖到WorkBook對象中后,系統占用內存從150M左右增加到700M以上,之后便維持在700M左右呈峰式不斷變化,這是因為數據倉庫的作用,對系統中封裝的Student對象的內存占用情況進行了良好的控制,又因為多生產者與多消費者的并發操作,系統中不斷產生的Student對象使用后被及時回收,系統吞吐量也有了顯著提升。相比于圖3中呈緩慢上升式的曲線而言,改進后的用戶模型更能適用于一定數據量的處理任務。

如表1不同數據規模下用戶模式改進前后的性能對比。

從上表中可以看出,生產者消費者模式對用戶模型性能的影響,改進后的用戶模型不僅保持了原來的高度抽象、易于理解和實現的優點,還解決了內存溢出的問題,在一定程度上對用戶模型進行了優化,擴展了用戶模型的應用場景。

但是使用用戶模型,系統的時間消耗受到極大影響,就比如表1中處理10萬條數據要花費327432ms,這樣的執行效率是不可觀的。影響時間的因素涉及到多方面,比如:多線程對資源的競爭、數據庫的多表處理、SQL優化問題、程序代碼的時間復雜度、I/O流的優化[5]等,由于文章篇幅有限且時間消耗問題不是本文討論的重點,所以不再詳細分析。

4 ?結論

經過對用戶模型在實際應用中的弊端以及產生原因的充分分析,展示了在不同數據規模下的系統性能消耗,利用生產者消費者模式的思想,對用戶模式進行重構,使得用戶模型中各個角色的職能分離、交互協作,并通過多線程并發編程的實現方式,解決了在一定數據量條件下的excel數據讀取問題,節省了系統的部分內存消耗,提升了業務處理效率。讓用戶模型在原有的高度抽象、易于理解等特性的基礎上,減輕了對系統資源的依賴,擴展了用戶模型的應用場景。雖然還不能完全解決用戶模型對 ?系統資源的依賴,但對用戶模型的內存占用做出了一定程度的優化,關于用戶模型的缺陷仍然在不斷研究。

參考文獻

[1] Apache POI docs[EB/OL]. [2019-4-16]. http://poi.apache. org/components/spreadsheet/index. html.

[2] 李騰. 批量數據優化處理框架的設計和實現[D]. 山東大學, 2015.

[3] 路勇. Java多線程同步問題分析[J]. 軟件, 2012, 33(4): 31-33

[4] 吉豪杰. 大數據時代下基于SSM框架的高校畢業生檔案管理系統的研發設計[J]. 軟件, 2018, 39(11): 151-158.

[5] 馬凱航, 高永明, 吳止鍰等. 大數據時代數據管理技術研究綜述[J]. 軟件, 2015, 36(10): 46-49

[6] 左大鵬, 徐薇. 基于Hadoop 處理小文件的優化策略[J]. 軟件, 2015, 36(2): 107-111

[7] 朝格. 淺談EXCEL與XML的數據交換[J]. 軟件, 2012, 33(5): 48-50

[8] 張強. 創建型模式在題型庫設計中的應用[J]. 軟件, 2012, 33(3): 69-71

[9] 胡泳霞. 基于內存模型的Java并發編程[J]. 電子測試, 2016(13): 89-90.

[10] 張志強, 王偉鈞, 施達. 一種大容量數據文件抽取算法的優化研究[J]. 成都大學學報(自然科學版), 2019(01): 52-55.

[11] 勞加慶, 葉飛躍, 張國平. 多線程技術對批量數據處理的優化[J]. 微型機與應用, 2001(04): 11-12.

主站蜘蛛池模板: 婷婷成人综合| 精品国产黑色丝袜高跟鞋| 毛片手机在线看| 国产性生交xxxxx免费| 国产一区二区免费播放| 欧美激情第一区| 91久久偷偷做嫩草影院电| 一级毛片在线播放| 色成人亚洲| 91精品日韩人妻无码久久| 国产免费怡红院视频| 亚洲an第二区国产精品| 色精品视频| 高清无码不卡视频| 欧美日韩国产精品va| 97se综合| 欧美日韩国产精品va| 黄片一区二区三区| 女人18毛片一级毛片在线| 亚洲激情99| 国产国产人成免费视频77777| 91国内在线观看| 在线免费观看a视频| 亚洲一区免费看| 久草视频福利在线观看| 国产激爽大片高清在线观看| 国产91视频免费观看| 国产成人8x视频一区二区| 中日韩一区二区三区中文免费视频 | 亚洲欧洲日本在线| 精品久久人人爽人人玩人人妻| 亚洲欧洲日本在线| 国产手机在线观看| 国产国模一区二区三区四区| 国产一区二区精品高清在线观看 | 久996视频精品免费观看| 亚洲一区网站| a级毛片在线免费| 亚洲国产成人精品青青草原| 日韩精品视频久久| 色婷婷视频在线| 红杏AV在线无码| 亚洲熟女偷拍| 免费无码AV片在线观看国产| 精品少妇人妻无码久久| 原味小视频在线www国产| 日韩国产 在线| 国产91成人| 欧美另类图片视频无弹跳第一页 | 婷婷色婷婷| 国产女人综合久久精品视| 色综合日本| 欧美日韩专区| 无码精品国产dvd在线观看9久 | 国产精品亚洲日韩AⅤ在线观看| 国产精品欧美亚洲韩国日本不卡| 美女被操91视频| 国产午夜人做人免费视频中文| 蜜桃臀无码内射一区二区三区| 美女无遮挡被啪啪到高潮免费| 久久久噜噜噜| 欧美国产综合视频| 国产迷奸在线看| 亚洲第一页在线观看| 91高清在线视频| 狠狠亚洲婷婷综合色香| 一级做a爰片久久免费| 91福利在线观看视频| 91视频首页| 精品国产一区二区三区在线观看 | 色欲色欲久久综合网| 一本综合久久| 国产h视频在线观看视频| 国产主播喷水| 欧美亚洲中文精品三区| 亚洲AV无码不卡无码| 亚洲午夜片| 国产99免费视频| 欧美一区日韩一区中文字幕页| 91热爆在线| 国产精品lululu在线观看 | 亚洲一区二区三区在线视频|