You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多线程下用Apache POI调用createRow()仍遇ConcurrentModificationException

解决Apache POI多线程创建Excel行时的ConcurrentModificationException问题

从你的异常栈和代码片段来看,问题的核心是你用的同步锁根本没起到互斥作用,导致多个线程仍在同时修改XSSFSheet对象,触发了并发修改异常。

为什么synchronized(this)无效?

你的每个线程都是RootQueryThread的独立实例,每个实例的this是不同的对象。当多个线程进入synchronized(this)代码块时,它们各自持有自己的锁,完全不会互相阻塞,多个线程还是能同时操作同一个sheet对象——这正是异常的根源。

正确的解决方案

要让所有线程共享同一个锁对象,确保同一时间只有一个线程操作sheet,这里有几种可行的方案:

方案1:以sheet对象作为锁(推荐)

因为所有线程操作的是同一个sheet实例,直接用它作为锁对象,能精准控制对sheet的互斥访问:

@Override
public void run() {
    ...
    // add row to excel sheet
    synchronized(sheet) { // 替换为sheet作为锁对象
        Row row = sheet.createRow(querySequence+1);
        row.createCell(0).setCellValue(rqResult.getRQName());
        // 其他单元格操作
    }
}

注意:必须确保所有线程引用的是同一个sheet实例,这个锁才能生效。

方案2:使用类级别的锁

如果sheet实例不方便直接传递给线程,也可以用RootQueryThread.class作为锁——类对象是全局唯一的,能保证所有线程都被同一把锁阻塞:

@Override
public void run() {
    ...
    // add row to excel sheet
    synchronized(RootQueryThread.class) {
        Row row = sheet.createRow(querySequence+1);
        row.createCell(0).setCellValue(rqResult.getRQName());
        // 其他单元格操作
    }
}

这种方式锁粒度比用sheet大,会锁住所有RootQueryThread实例,适合sheet实例难以获取的场景,但性能略逊于方案1。

方案3:提前创建所有行(性能最优)

如果你的行号是预先确定的(比如querySequence是连续的0、1、2...),可以在主线程中一次性创建好所有需要的行,线程只负责填充对应行的数据,彻底避免多线程修改sheet的场景:

// 主线程提前创建所有行
for (int i = 0; i < totalThreadCount; i++) {
    sheet.createRow(i + 1);
}

// 线程中仅填充数据,无需创建行
@Override
public void run() {
    ...
    Row row = sheet.getRow(querySequence + 1); // 直接获取已创建的行
    row.createCell(0).setCellValue(rqResult.getRQName());
    // 其他单元格操作
}

这种方式不需要线程等待锁,性能最好,但前提是你能提前确定需要创建的总行数。

额外提醒

Apache POI的XSSFSheet、Workbook等核心对象都不是线程安全的,任何修改操作(创建行/单元格、修改单元格内容等)都必须在单线程环境下执行,或者通过全局锁确保互斥访问。

内容的提问来源于stack exchange,提问作者Paul Ning

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:35:56