从Excel批量导入数据说到ForkJoin的原理

古之立大事者,不惟有超世之才,亦必有坚忍不拔之志。这篇文章主要讲述从Excel批量导入数据说到ForkJoin的原理相关的知识,希望能为你提供帮助。
前言前面我们介绍了EasyPOI,用其进行Excel导入导出,非常的方便,非常的简单。但是4.0.0 版本以及其之前的版本都是通过单线程的方式对Excel中的数据进行解析的。效率比较差。
今天我将做一个测试,5000条数据,分别使用EasyPOI的方式和自己手写ForkJoin的方式(多任务)的方式来导入,来比较这两者的性能差异。
测试前准备 1. 首先创建一个测试项目
首先我们需要创建一个测试项目,我这里新建了一个SpringBoot项目。
然后引入???easypoi??的依赖,本次引入的easyPOI的版本是4.0.0版本。

< !--easypoi-->
< dependency>
< groupId> cn.afterturn< /groupId>
< artifactId> easypoi-spring-boot-starter< /artifactId>
< version> 4.0.0< /version>
< /dependency>
< !--easypoi-->

2. 分别用两种方式实现导入
2.1:使用EasyPOI的方式
@Override
public String batchUploadStudent_easyPOI(MultipartFile file) throws Exception
long startTime = System.currentTimeMillis();
List< Student> studentList = ExcelImportUtil.importExcel(file.getInputStream(), Student.class, new ImportParams());
log.info("********通过EasyPOI读取文件总耗时是=,读取到的数据总条数是=", (System.currentTimeMillis() - startTime) + "毫秒", studentList.size());
return null;

【从Excel批量导入数据说到ForkJoin的原理】使用EasyPOI实现导入非常的简单,只需要调用importExcel方法即可。再此不在赘述。
2.2:自己手写Fork-Join的方式接下来,我们自己手写Fork-Join的方式来实现文件的解析。
  1. 解析单元格的方法,本demo是直接挨个读取每个单元格的,当然也可以通过注解的方式来实现。代码如下:
private List< Student> getData(Sheet sheet, int start, int end)
List< Student> mapList = new ArrayList< > ();
for (int i = start; i < = end; i++)
Student student = null;
try
Row row = sheet.getRow(i);
student = new Student();
student.setClassName(ExcelUtil.getKeyValue(row.getCell(0)));
student.setStudentName(ExcelUtil.getKeyValue(row.getCell(1)));
student.setStudentMobile(ExcelUtil.getKeyValue(row.getCell(2))); student.setIdCard(ExcelUtil.getKeyValue(row.getCell(3)));
student.setStudentNo(ExcelUtil.getKeyValue(row.getCell(4)));
student.setIdCard(ExcelUtil.getKeyValue(row.getCell(5)));
catch (Exception e)
log.info("***************税号=,文件名=,数据解析出现异常=", e);
continue;

mapList.add(student);

return mapList;


这个方法也是很简单,就是读取开始行到结束行之间的所有数据。每个单元格的读取,严格按照Excel的字段顺序来读。
2. 定义RecursiveTask类。
class JoinTask extends RecursiveTask< List< Student> >
//开始解读的行
private int start;
//结束解读的行
private int end;
//分页
private Sheet sheet;
//总的行数
private int total;

public JoinTask(int start, int end, Sheet sheet)
this.start = start;
this.end = end;
this.sheet = sheet;
this.total = sheet.getLastRowNum();


@Override
protected List< Student> compute()
//数据异常
if (start > end || total < end)
return new ArrayList< > (1);

//每200行一个解析
if (end - start < = 200)
return getData(sheet, start, end).stream().filter(DistinctUtil.distinctByKey(Student::getStudentNo)).collect(Collectors.toList());
else
//二分法,将数据平均分成两块
int mid = (start + end) / 2;
//递归调用,左边是序号小的那一块
JoinTask rightTask = new JoinTask(start, mid, sheet);
//递归调用,右边是数据大的那一块
JoinTask leftTask = new JoinTask(mid + 1, end, sheet);
//写法一
rightTask.fork();
List< Student> leftList =leftTask.compute();
List< Student> rightList = rightTask.join();
//写法二
//invokeAll(rightTask, leftTask);
//List< Student> leftList = leftTask.join();
//List< Student> rightList = rightTask.join();
//将左边和右边的数据合并
leftList.addAll(rightList);
return

    推荐阅读