You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

多线程调用Excel导出方法时遭遇java.lang.InterruptedException问题求助

解决多线程导出加密Excel时的InterruptedException问题

首先,InterruptedException一般是线程在阻塞状态(比如IO等待、资源获取等待)时被中断导致的。结合你的代码,主要问题集中在资源未正确释放线程安全隐患异常处理不规范上,下面一步步分析并给出修复方案:

一、核心问题拆解

  1. 资源未统一回收:Connection、Statement、ResultSet、SXSSFWorkbook以及各类IO流只在try块中关闭,一旦中途抛出异常就会跳过关闭逻辑,多线程下会造成资源泄漏,引发线程阻塞甚至被中断。
  2. SXSSFWorkbook临时文件未清理:SXSSFWorkbook会生成临时文件存储超出内存的行数据,不调用dispose()会导致临时文件堆积,占用系统资源,触发线程中断。
  3. 中断状态丢失:捕获InterruptedException后没有恢复线程的中断标记,导致后续逻辑无法感知线程被中断的状态。
  4. 加密阶段IO操作存在阻塞风险:文件写入和加密操作未确保流完全关闭,多线程下IO资源竞争可能导致线程长时间阻塞被中断。

二、修复后的完整代码

public void exportTOExcel(ExportReport exportReport) {
    Connection connection = null;
    ResultSet rs = null;
    Statement statement = null;
    SXSSFWorkbook wb = null;
    FileOutputStream out = null;
    POIFSFileSystem fs = null;
    OPCPackage opc = null;
    FileOutputStream fos = null;

    try {
        // 生成唯一文件名(毫秒级时间戳确保多线程下无重复)
        String fileName = exportReport.getReportName() + "_" + 
            new SimpleDateFormat("MMM_dd_yyyy_HH_mm_ss_SSS").format(Calendar.getInstance().getTime());
        String sheetName = exportReport.getReportName();
        String excelFile = PropertyLoader.FILE_DOWNLOAD_PATH + "\\" + fileName + ".xlsx";

        // 数据库连接与查询
        connection = DatabaseConnection.openDBConnection();
        statement = connection.createStatement();
        rs = statement.executeQuery(exportReport.getQuery());

        // 创建SXSSFWorkbook并配置单元格样式
        wb = new SXSSFWorkbook(100);
        Sheet workSheet = wb.createSheet(sheetName);
        DataFormat fmt = wb.createDataFormat();
        CellStyle cellStyle = wb.createCellStyle();
        cellStyle.setDataFormat(fmt.getFormat("@"));

        // 写入数据库结果到Excel
        int rowNumber = 1;
        ResultSetMetaData metaData = rs.getMetaData();
        int columnCount = metaData.getColumnCount();
        
        while (rs.next()) {
            Row row = workSheet.createRow(rowNumber);
            for (int i = 1; i <= columnCount; i++) {
                Object obj = rs.getObject(i);
                String value = obj != null ? String.valueOf(obj) : "";
                Cell cell = row.createCell(i - 1);
                cell.setCellStyle(cellStyle);
                cell.setCellValue(value);
            }
            rowNumber++;
        }

        // 自动调整列宽
        for (int i = 0; i < columnCount; i++) {
            workSheet.autoSizeColumn(i);
        }

        // 写入未加密Excel文件
        out = new FileOutputStream(excelFile);
        wb.write(out);

        // 对Excel文件进行加密处理
        fs = new POIFSFileSystem();
        EncryptionInfo info = new EncryptionInfo(fs, EncryptionMode.agile);
        Encryptor enc = info.getEncryptor();
        enc.confirmPassword(exportReport.getPassword());

        opc = OPCPackage.open(excelFile, PackageAccess.READ_WRITE);
        OutputStream os = enc.getDataStream(fs);
        opc.save(os);

        fos = new FileOutputStream(excelFile);
        fs.writeFilesystem(fos);

    } catch (InterruptedException e) {
        // 恢复线程中断状态,让上层逻辑能感知到中断事件
        Thread.currentThread().interrupt();
        e.printStackTrace();
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        // 统一关闭所有资源,彻底避免资源泄漏
        try {
            if (fos != null) fos.close();
            if (opc != null) opc.close();
            if (fs != null) fs.close();
            if (out != null) out.close();
            // 清理SXSSFWorkbook生成的临时文件
            if (wb != null) wb.dispose();
            if (rs != null) rs.close();
            if (statement != null) statement.close();
            if (connection != null) connection.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

三、额外优化建议

  • 替换线程不安全的日期格式化SimpleDateFormat是非线程安全的,多线程下可能出现日期格式错误,建议改用Java 8+的DateTimeFormatter
    DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MMM_dd_yyyy_HH_mm_ss_SSS");
    String fileName = exportReport.getReportName() + "_" + formatter.format(LocalDateTime.now());
    
  • 使用try-with-resources简化代码:Java 7+支持自动关闭资源的语法,能进一步确保资源释放,简化代码结构:
    try (Connection connection = DatabaseConnection.openDBConnection();
         Statement statement = connection.createStatement();
         ResultSet rs = statement.executeQuery(exportReport.getQuery())) {
        // 核心业务逻辑
    } catch (Exception e) {
        // 异常处理
    }
    
  • 添加超时控制:对数据库查询、文件IO操作添加超时设置,防止线程因长时间阻塞被中断。

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

火山引擎 最新活动