Process阻塞问题

本文探讨了一段Java代码中执行批处理文件时遇到的线程阻塞问题,通过分析Runtime.exec与ProcessBuilder的使用,揭示了标准输入流和错误流处理的重要性。作者分享了正确处理流的方法并介绍了ProcessBuilder的合并流优势,以避免JVM缓冲区满导致的阻塞问题。
摘要由CSDN通过智能技术生成

直接上出问题的代码:

public CommandResult executeFile(File file) {
        ArrayList<String> stdoutList = new ArrayList<String>();
        ArrayList<String> stderrList = new ArrayList<String>();

        try {
            Process p = Runtime.getRuntime().exec("cmd /c " + file.getName(), null, file.getParentFile());
            p.waitFor(); 
            p.getOutputStream().flush();
            p.getOutputStream().close();
            newThreadInputStream(p.getInputStream(), stdoutList).start();
            //newThreadInputStream(p.getErrorStream(), stderrList).start();
        } catch (IOException | InterruptedException e) {
            LOG.error(e.getMessage(), e);
            throw new RuntimeException(e);
        }
        return new CommandResult(stdoutList, stderrList);
    }

该方法,是通过Runtime.getRuntime().exec的方式去执行一个批处理文件,代码中可以看到,标准输入流是开启了一个线程去处理了,但是错误流部分的处理给注释掉了。然后,坑人的部分就开始了

在我本地去调用该方法执行批处理文件时,没有任何问题,然后在别人的环境执行时出现了线程阻塞的问题,通过加日志,发现线程在p.waitFor()这里阻塞住了,但我本地又没有事情。

通过查阅资料,得知,waitFor()的位置应该放在对输入流及错误流处理的后边执行,并且对输入流和错误流都要开启线程去处理,否则导致JVM缓冲区满了之后,就会出现阻塞。

修改代码后:

@Override
public CommandResult executeFile(File file) {
    ArrayList<String> stdoutList = new ArrayList<String>();
    ArrayList<String> stderrList = new ArrayList<String>();
    ArrayList<String> list = new ArrayList<String>();
    Process p = null;
    try {
        p = Runtime.getRuntime().exec("cmd /c " + file.getName(), null, file.getParentFile());
        p.getOutputStream().flush();
        p.getOutputStream().close();
        newThreadInputStream(p.getInputStream(), stdoutList).start();
        newThreadInputStream(p.getErrorStream(), stderrList).start();
        LOG.info("waitFor---before");
        p.waitFor();
        LOG.info("waitFor---after");
    } catch (IOException | InterruptedException e) {
        LOG.error("",e);
        throw new RuntimeException(e);
    } finally {
        try {
            p.destroy();
        } catch (Exception e){
            LOG.error("",e);
        }
    }
    return new CommandResult(stdoutList, list);
}

修改后,再次执行,正常。

其实这里我们在通过process执行批处理文件时,可以使用ProcessBuilder而不是Runtime.getRuntime().exec(),ProcessBuilder的好处就是可以合并错误流和输入流可以合并

ProcessBuilder processBuilder = new ProcessBuilder(file + File.separator+"backup.bat");
//将error流合并到标准中
processBuilder.redirectErrorStream(true);
Process process = processBuilder.start();

同样的,我们也需要去单开一个线程去处理这个流

process.getInputStream()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值