`
Everyday都不同
  • 浏览: 724247 次
  • 性别: Icon_minigender_1
  • 来自: 宇宙
社区版块
存档分类
最新评论

多线程写同一个excel文件(导出)

阅读更多

今天是2018.03.22,已经很久没有更新博客了。。这段时间一直挺忙的,也收获很多。最近一个excel导出的任务让我搞了好久,想想踩过的坑,就想上来小结一番。

------------------------------------------------------分割线------------------------------------------------------

 

前沿:对于Java党而言,poi是目前较为流行的对Microsoft Office格式档案读和写的功能的工具。但有时候单线程写一个数据量需求较大或较为耗时(如向excel写图片)的文件时,
如导出,往往太慢而超时。本文将以多线程导出一个excel文件为例(同一个sheet)来提高效率。

 

1.线程与任务
线程池:可以用线程池来管理线程,包括任务的分配和线程的回收。
任务:每个线程待处理的资源,在submit任务时,需保证每个任务不会互相重复。
线程:任务的执行者
(注:任务和线程的概念一定要区分开来!每个线程可以在执行完一次任务后,继续执行还未被处理的任务。线程池submit一个任务不代表会有一个线程立即去执行它)
如何保证所有任务都被执行完,才能够进行IO流的写入操作?
保证所有任务都执行完毕,可以用一个同步工具类:CountDownLatch来记录,初始化它的时候,size是任务数,每执行一次,就countDown。在所有任务执行完毕的代码逻辑后await,
即可防止还有任务没被线程执行完就往下执行,起到阻塞和等待的作用。

 

2.思路流程
首先,一个excel对象在poi里被定义为Workbook, 一个Workbook包含多个Sheet. 每个Sheet有Row, 每行又有Cell(单元格).
先把要往Sheet里写的所有数据按行区分,即先准备好要填充的数据集合(元素是每行的对象,代码略);
建立线程池,管理线程和分配任务:

ExecutorService es = Executors.newFixedThreadPool(40);
//线程执行任务的计数器,初始大小为待填充数据集的size
        CountDownLatch latch = new CountDownLatch(results.size());
        for(int i=0; i<results.size(); i++) {
          ExcelResultVo excelResultVo = results.get(i);
//          RowObj rowObj = groups.get(i);
          es.submit(new Runnable() {
            @Override
            public void run() {
              PoiWrite.writeData(excelResultVo, wb, sheet, patriarch, styleContent);
              latch.countDown();//每个任务执行完就countDown一次
            }
          });

        }
        latch.await();//阻塞,直到计数器的值为0,才让主线程往下执行
        es.shutdown();//关闭线程池

 因为操作的是同一个sheet,而sheet在addRow的时候底层是这样做的:

public void insertRow(RowRecord row) {
		// Integer integer = Integer.valueOf(row.getRowNumber());
		_rowRecords.put(Integer.valueOf(row.getRowNumber()), row);
		// Clear the cached values
		_rowRecordValues = null; 
		if ((row.getRowNumber() < _firstrow) || (_firstrow == -1)) {
			_firstrow = row.getRowNumber();
		}
		if ((row.getRowNumber() > _lastrow) || (_lastrow == -1)) {
			_lastrow = row.getRowNumber();
		}
	}

 /_rowRecords是一个Map<Integer, RowRecord>,而Map是线程非安全的,所以需要在addRow的时候加锁:

public static synchronized HSSFRow getRow(HSSFSheet sheet, int rownum) {
      return sheet.createRow(rownum);

  }

 另:往excel写图片需要图片的byte数组(图片的url地址转byte数组也比较耗时,可以用多线程先准转好,作为填充数据的一个属性),这里其实也是先createRow,再往指定单元格写图片了,大致语法:

HSSFClientAnchor anchor = new HSSFClientAnchor(0, 0,0 , 0, (short) col1, row1, (short) col2, row2);//这里其实createRow了
    HSSFPicture pict = patriarch.createPicture(anchor, wb.addPicture(imgData, HSSFWorkbook.PICTURE_TYPE_JPEG));

    pict.resize(1.0,1.0);

 所以写图片的方法也需要同步,不然会报错。。(血的教训)

经测试:单线程导出1000条数据(含1100张图片)需超过5min的时间,很容易超时。而多线程仅需20s以内!

注:一定要注意多线程操作同一对象的线程安全问题,如多线程操作同一List,一定要用线程安全的List如CopyOnWriteArrayList或用Collections.synchronizedList包装原始的ArrayList。
add的时候才能避免线程安全问题。

 

思考:之前一直对多线程停留在概念的理解上,实践的还较少,现在对于线程安全有更为直观的理解:是否不同线程执行了不同的资源(任务)而没有发生“多个线程重复执行同一资源”
的情况?比如本文的例子,是先把同一个sheet待填充的数据按Row划分,每个任务指定了Row的位置,每个线程负责将每行数据填充到指定位置而不会发生重复填充某一行的情况。这样多个线程执行
一个“大任务”划分出来的每个“小任务”,就能达到节省时间的目的。

分享到:
评论

相关推荐

    qt导出、操作excel(多线程)

    在Qt框架下,导出和操作Excel文件是一个常见的任务,特别是在处理大量数据时,为了保持用户界面的响应性,我们通常会使用多线程技术。本文将深入探讨如何使用Qt结合多线程来实现这个功能。 首先,让我们理解标题中...

    poi多线程大数据导出excel文件.zip

    本项目“poi多线程大数据导出excel文件”提供了一个解决方案,利用多线程来提高Excel的大数据导出效率。 Apache POI 3.1版本是较早的版本,而项目中使用了更新的4.1版本,这意味着它可能利用了更多优化和新特性。在...

    java多线程导出excel(千万级别)优化

    以下是一个简单的多线程导出步骤: 1. 数据预处理:将原始数据分割成多个子集,每个子集包含一定数量的行。 2. 创建线程池:使用ExecutorService创建一个线程池,设置合适的最大线程数,以平衡CPU利用率和内存消耗...

    多线程导出Excel(百万级别)_Java版优化.zip

    用开源 Apache POI 技术导出Excel,解决导出大数据出现OOM、栈溢出问题,此资源可实现百万级数据多线程分批导出Excel文件,不会内存溢出,生产环境已很稳定的使用者,所以用到的技术很核心、值得参考

    java导出30万数据量的excel(采用生成多个excel,最后打包zip)

    每个线程负责一个Excel文件的生成,这能充分利用多核处理器的计算能力,但需要注意线程安全问题,防止数据冲突。 4. **流式处理**: 使用Apache POI的SXSSFWorkbook类,它支持流式处理,可以在写入文件的同时释放...

    多个excel导出压缩成zip 文件 数据量大导出

    - 并行处理:如果系统资源允许,可以使用多线程或多进程同时处理多个Excel文件的导出和压缩,以提高整体效率。 - 存储优化:考虑使用云存储或分布式文件系统,如Hadoop HDFS,来处理和存储大数据量。 6. **安全性...

    Excel文件导入导出

    在IT行业中,Excel文件的导入和导出是一个常见的任务,特别是在数据分析、报表生成以及数据交换等领域。Excel由于其直观易用的界面和强大的表格处理功能,成为企业和个人处理数据的首选工具之一。以下是对"Excel文件...

    多线程导出Excel文件

    多线程导出Excel文件

    多个润乾报表,导出到一个excel中的不同sheet页中

    在导出时,我们可以指定每个报表作为一个单独的工作表,这样就可以在同一个Excel文件中看到多个报表。 4. **API调用**:为了实现将多个报表合并到一个Excel中,可能需要使用润乾报表提供的API接口。这些API允许...

    Java导出数据到Excel文件中(支持多页签)

    Java导出数据到Excel文件中,支持多页签形式,如通过Java导出一个名为“各部门人员列表”,然后在文件中有三个页签,分别为“研发部”、“综合部”、“财务部”。其中这三个页签里面的数据就是通过Java导出到Excel...

    使用poi方式导出excel(分多个sheet导出)

    本教程将详细介绍如何利用Apache POI库来实现使用多个Sheet(工作簿)导出一个Excel文件的功能。 一、Apache POI简介 Apache POI 是一个开源项目,提供API来读取、写入和修改Microsoft Office文件格式,如Word(DOC...

    springboot 实现后端接口操作Excel的导出、批量导入功能

    对于处理Excel文件,我们可以使用Apache POI库,这是一个用于读写Microsoft Office格式文件的Java API。在`pom.xml`文件中添加以下依赖: ```xml &lt;groupId&gt;org.apache.poi &lt;artifactId&gt;poi &lt;version&gt;4.1.2 ...

    Excel文件的导入(异步线程)

    本知识点主要探讨如何高效地实现Excel文件的导入,特别是利用异步线程技术来提升性能。异步线程在多任务环境中,能够使得程序在等待IO操作完成时,不会阻塞主线程,从而提高用户体验。 首先,我们需要了解Excel文件...

    导入导出工具支持excel文件.rar

    3. 数据导出:数据导出则是将Excel中的数据保存到其他格式或应用程序中,如CSV、XML、PDF或另一个Excel文件。用户可以直接通过“另存为”选项完成此操作,也可以通过VBA编写代码实现自动化,例如定期自动导出数据到...

    海量千万级Excel导出源码-自动分sheet

    2. 多线程或异步处理:对于大规模数据,多线程或多进程并行处理可以提高效率,尤其是当硬件资源充足时。 3. 分页算法:在决定何时创建新的工作表时,需要一个合理的分页策略,确保每个工作表的数据量均衡且不超过...

    java解决大批量数据导出Excel产生内存溢出的方案

    - 使用多线程或者异步处理,将数据导出任务分解成多个子任务,每个任务处理一部分数据,从而分散内存压力。 9. **内存分析和优化**: - 使用内存分析工具(如VisualVM或YourKit Java Profiler)监控内存使用情况...

    基于springboot和poi实现单线程和多线程导出Excel之单线程导出的代码

    基于springboot和poi实现单线程和多线程导出Excel之单线程导出的代码。对于工作中导出Excel数据的小伙伴有一定的帮助作用。代码比较简单易学不需要太深的底层只是既可以使用,代码完全可以直接复制粘贴即可能在自己...

    导出到excel很快的类

    "XLSFile"可能表示这个单元专门处理XLS(早期版本的Excel)格式,或者是一个通用术语,用于处理包括XLSX在内的所有Excel文件类型。 最后,“UntObject.pas”同样是一个源代码单元,可能包含了与导出相关的特定对象...

Global site tag (gtag.js) - Google Analytics