Java使用Process执行命令时,waitFor()方法死锁阻塞

一:在使用Java的Process类时,调用waitFor方法可能会导致线程阻塞无法返回,例如,解压压缩包时,压缩包有密码,而命令中没有设置密码

public void processExec(String command) {
    Process ps = Runtime.getRuntime().exec(command);
    ps.waitFor();
    ...
    ...
}

二:这种情况是因为:

主进程中调用Runtime.exec会创建一个子进程,用于执行cmd命令。子进程创建后会和主进程分别独立运行。
因为主进程需要等待脚本执行完成,然后对命令返回值或输出进行处理,所以这里主进程调用Process.waitfor等待子进程完成

运行此cmd命令可以知道:子进程执行过程就是打印信息。主进程中可以通过Process.getInputStream和Process.getErrorStream获取并处理。
这时候子进程不断向主进程发生数据,而主进程调用Process.waitfor后已挂起。当前子进程和主进程之间的缓冲区塞满后,子进程不能继续写数据,然后也会挂起。
这样子进程等待主进程读取数据,主进程等待子进程结束,两个进程相互等待,最终导致死锁。

三:这种时候需要添加一个工具类或者配置类,不断的读取消耗缓冲区的数据,以至子进程不会挂起,并且设置超时

ProcessConfig配置类,start方法通过线程池创建线程

@Slf4j
@Data
public class ProcessConfig implements Runnable {

    private static final int SCHEDULE_SIZE = Runtime.getRuntime().availableProcessors();

    private ScheduledExecutorService scheduledExecutorService;

    private String character = "utf-8";

    private InputStream inputStream;

    private String errStr;

    public String getErrStr() {
        return errStr;
    }


    public void setErrStr(String errStr) {
        this.errStr = errStr;
    }


    public ProcessConfig(InputStream inputStream) {
        this.inputStream = inputStream;
        scheduledExecutorService = null;
    }

    /**
     * 以utf-8的编码格式去读取文件
     */
    @Override
    public void run() {
        BufferedReader br = null;
        try {
            br = new BufferedReader(new InputStreamReader(inputStream, character));
            String line = null;
            StringBuilder sb = new StringBuilder();
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            errStr = sb.toString();
        } catch (UnsupportedEncodingException e) {
            log.error("执行解IO操作异常!" + e);
        } catch (IOException e) {
            log.error("执行解IO操作异常!" + e);
        }finally{
            if (br != null) {
                try {
                    br.close();
                } catch (IOException e) {
                    log.error("执行解IO操作异常!" + e);
                }
            }
            if (inputStream != null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    log.error("执行解IO操作异常!" + e);
                }
            }
        }
    }

    public void start() {
        scheduledExecutorService = new ScheduledThreadPoolExecutor(SCHEDULE_SIZE,
                r -> {
                    Thread thread = new Thread(r);
                    thread.setDaemon(true);
                    thread.setName("");
                    return thread;
                });
    }

}

原来添加代码异常处理

public void processExec(String command) {
    Process ps = null;
    try {
        ps = Runtime.getRuntime().exec(command);
        ProcessConfig pu = new ProcessConfig(ps.getErrorStream());
        ps.getOutputStream().close();
        ps.waitFor();
        String errorStr = pu.getErrStr();
        if (errorStr != null) {
            log.error("执行解压操作异常!" + errorStr);
        }
    } catch (IOException e) {
        log.error("执行解IO操作异常!" + e);
    } catch (InterruptedException e) {
        log.error("执行解压打断异常!" + e);
    } finally {
        if (ps != null) {
            ps.destroy();
        }
    }
}

参考链接:Runtime 调用Process.waitfor导致的阻塞问题 | 弦上的梦

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值