Apache POI Workbook中的多线程处理

4

我希望在多线程环境下将数据写入HSSFWorkBook或XSSFWorkBook。每个线程将会在同一个或不同的工作表中进行修改。

try {
    String filePath="C:/Test.xlsx";
    FileInputStream fileInputStream = new FileInputStream(filePath);
    Workbook workbook = new XSSFWorkbook(fileInputStream);
    FileOutputStream fos = new FileOutputStream(filePath);
    workbook.write(fos);
    fos.close();
    fileInputStream.close();
} catch(Exception e) {
    e.printStackTrace();
    System.out.println(e.getMessage());
}

在SOAPUI中使用MultiThreading进行LoadTest测试时,我在以下行代码处遇到了异常:

Workbook workbook = new XSSFWorkbook(fileInputStream);

异常情况如下所示:
org.apache.poi.POIXMLException: org.apache.poi.openxml4j.exceptions.InvalidFormatException: Package should contain a content type part [M1.13]

看起来像是文件格式问题。我建议先用单线程进行测试。 - Astra Bear
只是提醒一下:当您同时打开同一文件时,一定要非常准确!特别是在修改操作方面! - iMysak
4个回答

13

请注意:Apache POI明确不支持对同一工作簿对象的多线程访问!这是因为有一些结构是在工作簿级别上处理的,例如样式、注释等。

如果您尝试单纯地这样做,您将遇到难以理解的错误和损坏的文档。

它所保证的唯一是,在不同线程中的分离工作簿将正常工作,即没有任何线程不安全的全局状态。

唯一应该起作用的方式是通过同步块同步每个对工作簿的访问:

synchronized (workbook) {
    ... access the sheet and the contents
}

只读访问可能有效,但是 Apache POI 不能保证同时对同一工作簿进行并发读取的工作。

更新:现在有一个相应的FAQ 条目也声明了这一点。


1

有一个解决方案...我将行创建到一个数组中...然后在线程中使用该数组,你应该避免在线程中使用创建行的方法,因为这种方法会受到线程安全的监控。

抱歉,我不会说英语...我正在努力学习。

static int count=0;

  

    
    XSSFWorkbook workbook = new XSSFWorkbook();
    XSSFSheet sheet = workbook.createSheet("Products");
    sheet.createFreezePane(0, 1); // this will freeze first five rows
    
    int rowCount = 0;
    
    rowCount = getExcelTittle(sucsCompetencia,sucsProveedor, sucsPuntoVenta, sheet, rowCount);  


    // trucazo para multithread
    List<Row> r = new ArrayList<Row>();
    //List<Drawing> h = new ArrayList<Drawing>();
    
    //cell creation
    for ( int j = 0 ; j < ps.size() + 500 ; j++) {
        r.add(sheet.createRow(j));
        //h.add(sheet.createDrawingPatriarch());

    }
    // cells creation .. its a good idea do it 
    Map<String,Cell> cellMap = new HashMap<String,Cell>();
    for (int j = 0 ; j < r.size() ; j++) {
        for ( int  i = 0 ; i < 9 + sucsCompetencia.size() + sucsProveedor.size() + sucsPuntoVenta.size() ; i++) {
            cellMap.put( j + "-" + i, r.get(j).createCell(i));
        }


    }       
    
    Stream<Producto> arrStream = ps.parallelStream();
    arrStream.forEach(p->
                        {   
                            count++;
        int contadorPropio = count;
        
        if (contadorPropio % 1000 == 0)  log.info("* Procesando Generacion Excel " + contadorPropio + " de " + ps.size());
        
       // if (rowCount == 1000 ) break;
        

        
        int columnCount = 0;
        
        
        {
            //IMPORTANTEEEEEEEE
            Cell cell = cellMap.get(contadorPropio + "-" + columnCount++); 
            cell.setCellValue(p.getIdProducto());
        }     

1
您可以通过读取文件C:/Test.xlsx来创建一个XSSFWorkbook;调用的构造函数是XSSFWorkbook(InputStream),它构造了一个OPCPackage。这个C:/Test.xlsx文件必须是有效的,即不损坏,不为空。
如果您想创建一个新的工作簿,您不应该读取一个空文件,而是使用适当的构造函数。
关于多线程,我没有测试过,但不同线程可以做的事情有限制(例如每个XSSheet一个线程)。请参阅邮件存档

-1
在主线程中创建工作簿,工作簿创建工作表并将其分配给工作线程。主线程应等待所有工作线程完成。主线程将工作簿中的数据刷新到流中,然后将流写入文件。
如果您需要带有示例的源代码,请发送电子邮件至ramesh.niwas@gmail.com,我会与您分享。

请参考我的博客:https://rameshvanka.blogspot.com/2020/03/excel-with-multi-threaded-way.html - rameshvanka

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接