项目里有一个导出功能,但随着数据量大量上涨,导出时间长到不可忍受,遂重写此接口,多线程导出的代码并不复杂,每页有一条线程负责写入,利用线程池去调度,用countdownLatch保证在所有数据写完后再写入文件。修改后,导出所有数据时间限制在了一分钟以内。但是由于poi自身为了资源高效利用,同一个workbook里的cell,setCellValue采用的是同一个SharedStringTable对象,由于多个线程同时使用而没有加以限制,因此产生了线程不安全的问题。

有三种解决的办法

  1. 获取poi源码,更改为线程安全后重新打包替换
  2. 在调用setCellValue的时候获取到SharedStringTable对象,然后加锁

    前两种方法可以参考【这个链接](https://blog.csdn.net/vatrenoludilo/article/details/121951681)

    第一个方法我从GitHub上把源码下下来后报了一堆莫名其妙的错,就放弃了

    第二种方法对setCellValue加锁,由于要导出的excel列很多,而且很多列需要单独处理,所以要么加锁粒度大,要么加锁代码负责,这都是我不想要的

    再来仔细分析一下问题

    是因为SharedStringTable类的addEntry()没有加锁导致的,既然不能修改源码,那么能不能继承这个类,然后在子类加锁,最后把原来使用的对象换成子类的对象。

    子类的实现很简单
/**
* @author TestLove
* @version 1.0
* @date 2022/2/21 22:25
* @Description: null
*/
public class CustomSharedStringsTable extends SharedStringsTable {
@Override
public synchronized int addSharedStringItem(RichTextString string){
return super.addSharedStringItem(string);
} }

如何替换呢?利用反射,在workbook类中,SharedStringTable对象的名字叫sharedStringTable,

  Field field = workBook.getClass().getDeclaredField("sharedStringSource");
field.setAccessible(true);
field.set(workBook,customSharedStringsTable);

但是仅仅这样替换是不够的,虽然能导出,但导出的文件无法打开。

于是继续看源码,sharedStringTable这个对象到底是怎么来的

从workbook的构造方法开始看,一层层调用后最后落脚点在onWorkbookCreate这个私有方法

private void onWorkbookCreate() {
workbook = CTWorkbook.Factory.newInstance(); // don't EVER use the 1904 date system
CTWorkbookPr workbookPr = workbook.addNewWorkbookPr();
workbookPr.setDate1904(false); setBookViewsIfMissing();
workbook.addNewSheets(); POIXMLProperties.ExtendedProperties expProps = getProperties().getExtendedProperties();
expProps.getUnderlyingProperties().setApplication(DOCUMENT_CREATOR); sharedStringSource = (SharedStringsTable)createRelationship(XSSFRelation.SHARED_STRINGS, this.xssfFactory);
stylesSource = (StylesTable)createRelationship(XSSFRelation.STYLES, this.xssfFactory);
stylesSource.setWorkbook(this); namedRanges = new ArrayList<>();
namedRangesByName = new ArrayListValuedHashMap<>();
sheets = new ArrayList<>();
pivotTables = new ArrayList<>();
}

createRelationship(XSSFRelation.SHARED_STRINGS, this.xssfFactory),这一句返回的是POIXMLDocumentPart对象,但SharedStringTable继承了这个类,因此可以进行类型转换.

观察其他的方法名,我们可以发现有getRelationByID这一类的方法,点进去发现返回值从一个map中来,

于是猜想,需要把这个map里存储的value一并给替换掉,才能保证一致性,使文件能够正常打开.但目前又不知道id究竟是什么,于是继续采用反射获取到map,并打印出里面的内容.注意,这里的value并不是POIXMLDocumentPart而是POIXMLDocumentPart.RelationPart,所以说还要经过一步转换才能获取到想要的对象

但是只是把customSharedStringtable设置到map里会导致写入文件时报空指针,猜想是一些属性没有设置的缘故,于是利用反射,把原来的字段复制到当前对象的字段中.

for (Field declaredField1 : declaredFields1) {
System.out.println(declaredField1.getName());
for (Field declaredField : declaredFields) {
declaredField1.setAccessible(true);
declaredField.setAccessible(true);
if(declaredField1.getName().equals(declaredField.getName())
&&!declaredField.getName().equals("logger")){ declaredField.set(customSharedStringsTable,declaredField1.get(documentPart1));
} }

至此,问题解决.

解决POI多线程导出时数据错乱问题的更多相关文章

  1. $ListView的优化机制和滑动时数据错乱的讨论

    Refer:http://www.myexception.cn/mobile/1612364.html (一)Android ListView的基本用法 1.创建一个实体类Person,为其添加Get ...

  2. POI通用导出Excel数据(包括样式设计)

    前言 前一段时间我写过通用的导入Excel,前几天也写了导出pdf格式的,还有我之前搞得导出Word,我在之前的博客也都介绍了导出和导入是一个道理,无非是一个获取一个是赋值.昨天有一位同仁看了我的Ex ...

  3. POI 导入导出时异常[java.io.IOException: Broken pipe]

    使用用POI导出文件时抛出异常java.io.IOException: Broken pipe ERROR: 'java.io.IOException: Broken pipe' org.apache ...

  4. POI3.8解决导出大数据量excel文件时内存溢出的问题

    POI3.8的SXSSF包是XSSF的一个扩展版本,支持流处理,在生成大数据量的电子表格且堆空间有限时使用.SXSSF通过限制内存中可访问的记录行数来实现其低内存利用,当达到限定值时,新一行数据的加入 ...

  5. 如何解决在ie下,Echarts多次使用setOption更改数据时,数据错乱问题

    一.问题描述 根据用户的操作,通过Ajax请求,获取某段时间内的某数据趋势折线图数据.用户切换数据项或更改时间段时,ie中渲染的折线图包含了上一次获取的数据,导致数据错乱,如下图所示: 二.代码 数据 ...

  6. JAVA使用POI如何导出百万级别数据(转)

    https://blog.csdn.net/happyljw/article/details/52809244   用过POI的人都知道,在POI以前的版本中并不支持大数据量的处理,如果数据量过多还会 ...

  7. JAVA使用POI如何导出百万级别数据

    用过POI的人都知道,在POI以前的版本中并不支持大数据量的处理,如果数据量过多还会常报OOM错误,这时候调整JVM的配置参数也不是一个好对策(注:jdk在32位系统中支持的内存不能超过2个G,而在6 ...

  8. JAVA使用POI如何导出百万级别数据(转载)

    用过POI的人都知道,在POI以前的版本中并不支持大数据量的处理,如果数据量过多还会常报OOM错误,这时候调整JVM的配置参数也不是一个好对策(注:jdk在32位系统中支持的内存不能超过2个G,而在6 ...

  9. java解决poi导出excel文字水印,导出excel不可操作问题

    首先需求是用户提出导出excel数据需使用水印备注其用途: 其实就是在导出excel的同时带有自定义文字水印的导出. 那么我们首先想到的肯定是以一个什么样的思路去解决该问题,首先查找poi导出exce ...

随机推荐

  1. SpringBoot整合Elasticsearch+ik分词器+kibana

    话不多说直接开整 首先是版本对应,SpringBoot和ES之间的版本必须要按照官方给的对照表进行安装,最新版本对照表如下: (官网链接:https://docs.spring.io/spring-d ...

  2. 学习javaScript必知必会(3)~数组(数组创建,for...in遍历,辅助函数,高级函数filter、map、reduce)

    一.数组: 1.js是弱语言,js中的数组定义时:不用指定数据类型.不用功指定数组长度:数组可以存储任何数据类型的数据 2.数组定义的[ ] 的实质: [] = new Array(); {} = n ...

  3. 《剑指offer》面试题26. 树的子结构

    问题描述 输入两棵二叉树A和B,判断B是不是A的子结构.(约定空树不是任意一个树的子结构) B是A的子结构, 即 A中有出现和B相同的结构和节点值. 例如: 给定的树 A:      3     / ...

  4. DASCTF-Sept-X-浙江工业大学秋季挑战赛-pwn-wp

    目录 DASCTF-Sept-X-浙江工业大学秋季挑战赛-pwn-wp 总结 datasystem check分析 漏洞点 利用思路 exp hehepwn 漏洞点 exp hahapwn 漏洞点 e ...

  5. 【刷题-LeetCode】275. H-Index II

    H-Index II Given an array of citations sorted in ascending order (each citation is a non-negative in ...

  6. 【重构前端知识体系之HTML】讲讲对HTML5的一大特性——语义化的理解

    [重构前端知识体系之HTML]讲讲对HTML5的一大特性--语义化的理解 引言 在讲什么是语义化之前,先看看语义化的背景. 在之前的文章中提到HTML最重要的特性,那就是标签.但是项目一大,标签多的看 ...

  7. Cesium和Kaarta用高分辨率激光雷达可视化室内和地下环境

    Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ Cesium使急救人员和军事操作人员更容易快速评估和了解密集和不 ...

  8. 利用SelectPdf插件将网页生成PDF

    简介 适用于.NET Framework和.NET Core的HTML至PDF转换器 SelectPdf提供的在线html到pdf转换器使用.NET的Select.Pdf库中的html到pdf转换器. ...

  9. python15day

    昨日回顾 装饰器:完美的呈现了开放封闭原则.本质:闭包. def wrapper(f): def inner(*args,**kwargs): '''在执行被装饰函数之前,想写什么代码写什么代码''' ...

  10. Java线上问题排查神器Arthas实战分析

    概述 背景 是不是在实际开发工作当中经常碰到自己写的代码在开发.测试环境行云流水稳得一笔,可一到线上就经常不是缺这个就是少那个反正就是一顿报错抽风似的,线上调试代码又很麻烦,让人头疼得抓狂:而且deb ...