背景:
数据组同事需要一个小工具帮组他们处理一些数据。首先他们的原数据是csv文件的,需要我们把里面的数据和别的系统里面的数据逐条匹配完善后返回给他们一个csv文件。原数据的csv一般3-5万条数据量(单列的)。
解决方案:
首先读取csv文件,生成待处理数据集合。然后把该数据集合分片,以使用多线程进行匹配处理。最后通过POI生成csv输出。(一开始同事没有使用多线程,虽然数据量不算大,但是匹配和处理过程需要多次调用别的系统的接口服务,所以速度上还是有点慢的。他问我如何优化,这才引入多线程,处理数据速度明显提升。)
具体实现:
0、方法主干:
FileInputStream inStream = new FileInputStream(filename);
List
//保证线程安全,数据不会丢失
List
try {
long endTime1 = System.currentTimeMillis();
float endTimePp = (float) (endTime1 - startTime) / 1000;
logger.info("匹配结束时间:" + endTimePp + "s");
//创建临时csv文件
long startTim = System.currentTimeMillis();
logger.info("文件写入开始时间:" + startTim);
logger.info("文件写入开始时间writeCsv==:" + writeCsv.size());
CsvUtil.createTempFile(writeCsv, urlCharset);//通过POI工具类创建csv文件并写入数据
long endTime = System.currentTimeMillis();
float excTime = (float) (endTime - startTim) / 1000;
logger.info("文件写入结束时间:" + excTime + "s");
} catch (IOException e) {
e.printStackTrace();
System.out.println("导出失败");
}
1、首先读取csv文件。
public static List
List
try {
BufferedReader reader = new BufferedReader(new InputStreamReader(inStream, "UTF-8"));
reader.readLine();//第一行信息,为标题信息,不用,如果需要,注释掉
String line = null;
int num = 0;
while ((line = reader.readLine()) != null) {
num++;
String item[] = line.split(",");//CSV格式文件为逗号分隔符文件,这里根据逗号切分
medicalWhiteListVOs.add(getValue(item, 0));
}
logger.info("**一共行数**:" + num);
} catch (Exception e) {
e.printStackTrace();
}
return medicalWhiteListVOs;
}
2、把上面获得的数据list分片,切割成多个list。
/**
* 将一个list均分成n个list,主要通过偏移量来实现的
* @param source 原list
* @param n 要分成几个list
* @return
*/
public static > averageAssign(List
List> result=new ArrayList
>();
int remaider=source.size()%n; //(先计算出余数)
int number=source.size()/n; //然后是商
int offset=0;//偏移量
for(int i=0;i
if(remaider>0){
value=source.subList(i*number+offset, (i+1)*number+offset+1);
remaider--;
offset++;
}else{
value=source.subList(i*number+offset, (i+1)*number+offset);
}
result.add(value);
}
return result;
}
3、创建线程池,并循环创建多线程。多个线程一起处理分割后的数据集合,然后 写入同一个大的list。
//创建大的数据集合用于接收各个线程处理后的数据,由于ArrayList是线程不安全的所以为了保证线程安全,数据不会丢失,在此对ArrayList进行序列化,不然会有数据丢失。
List
//创建线程池
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(thredList.size());
for (int i = 0; i < thredList.size(); i++) {
List
MyThread myThread = new MyThread(i, writeCsv, list, tokenUrl, token);//具体处理逻辑都在线程的run方法中
Thread tempThread = new Thread(myThread);
tempThread.setName("线程" + i);
fixedThreadPool.execute(tempThread);
}
fixedThreadPool.shutdown();
while (true) {//等待所有任务都执行结束
if (fixedThreadPool.isTerminated()) {//所有的子线程都结束了
System.out.println("共耗时:"+(System.currentTimeMillis()-startTime)/1000.0+"s");
break;
}
}
4、线程的创建。
/**
* 自定义线程
* */
class MyThread implements Runnable {
private int i; // 第几个线程
private List
private String token;
private String tokenUrl;
private List
//自定义构造方法
public MyThread( int i,List
public void run() {
try {
System.out.println("=====================进入线程得方法================" +Thread.currentThread().getName() + ": " + i +"--------------"+csvList.size());
for (int j = 0; j < csvList.size(); j++) {
String str= csvList.get(j);//获取待处理数据
。。。。。。具体的处理实现需要根据自己的业务需求来编码
}
writeCsv.add(map);
}
}catch (Exception e) {
e.printStackTrace();
System.out.println("导出失败");
}
}
}
5、POI写入csv。
/**
* 创建临时的csv文件
* @return
* @throws IOException
*/
public static File createTempFile(List
如果有需要输出exl表格的同学可参见以下链接:https://blog.csdn.net/xiaolegeaizy/article/details/90517191
及表格样式设置:https://blog.csdn.net/xiaolegeaizy/article/details/101058706
至此整个小工具的功能算是完成了。处理数据速度相对原来单线程提升了好几倍,3万数据只要半分钟左右。
工具源码:https://download.csdn.net/download/xiaolegeaizy/11855871