关闭流之后,为什么正处于转换中的线程还能继续执行

我有一个转换非常耗时的excel,我想通过一下代码的方式,在线程中转换这个文件,当一定时间之后无法获取到结果时,关闭这个流,使线程强制报错,但是以下代码 ,关闭流之后,线程仍在运行,没有抛出异常,我发现,如果我在创建线程执行任务之后立即关闭流,则线程也会立即报错并结束(请看我注释掉的那一段代码),为什么等待一段时间之后在关闭就无效

@Test
public void testone() throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(1);
String s = “209aab3c-6f1b-3c97-81cf-8eecffae490e”;
FileInputStream fileInputStream = new FileInputStream(“C:\Users\Administrator\Downloads\转换超时的文件 (1)\xlsx\” + s + “.xlsx”);
FileOutputStream fileOutputStream = new FileOutputStream(“C:\Users\Administrator\Downloads\转换超时的文件 (1)\xlsxpdf\” + s + “.pdf”);

    Future<String> future = executorService.submit(() -> {
        try {
            long start = System.currentTimeMillis();

            Workbook workbook = new Workbook(fileInputStream);
            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
            // 缩放到一个页面(如果列太多,太长)
            pdfSaveOptions.setAllColumnsInOnePagePerSheet(true);
            workbook.save(fileOutputStream, pdfSaveOptions);

            long end = System.currentTimeMillis();
            fileInputStream.close();
            fileOutputStream.close();
            System.out.println(" 耗时:" + (end - start));
            return "成功";
        } catch (Exception e) {
            System.out.println("转换错误");
            throw new RuntimeException(e);
        }
    });
    //当我打开这句代码时,转换线程会立即报错,因为流被关闭

// fileInputStream.close();
try{
//等待一定时间获取结果,如果报错,则立即关闭线程
String result = future.get(1000 * 5, TimeUnit.MILLISECONDS);
} catch (Exception e) {
System.out.println(“获取转结果异常:” + e);
//然而,在此处关闭流,转换线程却没有立即报错,仍然在执行,占用了内存
fileInputStream.close();
}
System.out.println(“结束”);
}

是否Workbook 对象会读取流到字节数组中?所以关闭流不会影响线程,那我该如何关闭这种超时的线程呢

@humanhuman

在最新的版本中,Workbook加载完毕以后,输入stream不再使用。所以,如果你在workbook加载完毕,关闭输入stream,是不会报错的。

请使用Workbook.InterruptMonitor :

另外,如果你的源文件不大或者没有太多的页,转换却非常耗时,你可以把源文件分享给我们,以便我们改进。

@Test
public void testone() throws Exception {
    ExecutorService executorService = Executors.newFixedThreadPool(1);
    String s = "209aab3c-6f1b-3c97-81cf-8eecffae490e";
    FileInputStream fileInputStream = new FileInputStream("C:\\Users\\Administrator\\Downloads\\转换超时的文件 (1)\\xlsx\\" + s + ".xlsx");
    FileOutputStream fileOutputStream = new FileOutputStream("C:\\Users\\Administrator\\Downloads\\转换超时的文件 (1)\\xlsxpdf\\" + s + ".pdf");
    Workbook workbook = new Workbook(fileInputStream);
    InterruptMonitor interruptMonitor = new InterruptMonitor();
    workbook.setInterruptMonitor(interruptMonitor);
    Future<String> future = executorService.submit(() -> {
        try {
            long start = System.currentTimeMillis();
            PdfSaveOptions pdfSaveOptions = new PdfSaveOptions();
            // 缩放到一个页面(如果列太多,太长)
            pdfSaveOptions.setAllColumnsInOnePagePerSheet(true);
            workbook.save(fileOutputStream, pdfSaveOptions);
            long end = System.currentTimeMillis();
            fileInputStream.close();
            fileOutputStream.close();
            System.out.println(" 耗时:" + (end - start));
            return "成功";
        } catch (Exception e) {
            //没有走到这一行代码
            System.out.println("转换错误");
            throw new RuntimeException(e);
        }
    });

    try{
        String result = future.get(1000 * 5, TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        System.out.println("获取转结果异常:" + e);
        interruptMonitor.interrupt();
    }
    System.out.println("结束");
}

我修改了代码, 使用了interruptMonitor.interrupt();这个方法,但是转换线程还是没有报错,我不知道转换线程到底是否结束

@humanhuman

调用interruptMonitor.interrupt();后,可能不会立即抛异常。请尝试像文档中示例代码那样,在父线程中等待conversion线程的结束。

调用interruptMonitor.interrupt();后,还需要等待conversion线程结束,那conversion线程如果执行很久,不是会等待很久吗,那interruptMonitor.interrupt();的作用不是没有生效吗,我的想法是在一个固定时间之后,直接终止conversion线程,而不是等待conversion线程结束

@humanhuman,
一般情况下,我们需要慎重考虑检查interruption的频率以平衡响应速度和程序性能。 如果我们对interruption的检查过于频繁,则monitor本身会给用户程序带来很大的性能影响。而检查的粒度过大时则会出现响应不及时的情况。

对于您的具体情况,您可以提供重现这个问题的代码和文件,我们来检查是否可以改进对interruption的判断,从而为您提供更好的响应效果。

而对于终止线程,只能由您通过线程相关的接口直接对您生成的线程进行操作,我们无法直接去判断和终止用户的线程。

@humanhuman

默认情况下,我们内部检测到interruptMonitor.interrupt();后,workbook.Save方法会抛异常。
只不过,由于检查的粒度的大小问题,从你调用interruptMonitor.interrupt();到我们内部检测到interruptMonitor.interrupt();有一定的时间间隔。