168Java调用EXE程序出现阻塞问题的解决方法

Posted zhangchao19890805

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了168Java调用EXE程序出现阻塞问题的解决方法相关的知识,希望对你有一定的参考价值。

错误写法


import java.io.*;

public class OldCmdUtils 
    
    /**
     * 执行系统命令, 返回执行结果
     *
     * @param cmd 需要执行的命令
     * @param dir 执行命令的子进程的工作目录, null 表示和当前主进程工作目录相同
     */
    public static String execCmd(String cmd, File dir) 
        StringBuffer result = new StringBuffer();

        Process process = null;
        BufferedReader bufrIn = null;
        BufferedReader bufrError = null;

        try 
            // 执行命令, 返回一个子进程对象(命令在子进程中执行)
            process = Runtime.getRuntime().exec(cmd, null, dir);

            // 方法阻塞, 等待命令执行完成(成功会返回0)
            process.waitFor();

            // 获取命令执行结果, 有两个结果: 正常的输出 和 错误的输出(PS: 子进程的输出就是主进程的输入)
            bufrIn = new BufferedReader(new InputStreamReader(process.getInputStream(), "UTF-8"));
            bufrError = new BufferedReader(new InputStreamReader(process.getErrorStream(), "UTF-8"));

            // 读取输出
            String line = null;
            while ((line = bufrIn.readLine()) != null) 
                result.append(line).append('\\n');
            
            while ((line = bufrError.readLine()) != null) 
                line = line.trim();
                result.append(line).append('\\n');
            

         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         catch (InterruptedException e) 
            e.printStackTrace();
         finally 
            closeStream(bufrIn);
            closeStream(bufrError);

            // 销毁子进程
            if (process != null) 
                process.destroy();
            
        

        // 返回执行结果
        return result.toString();
    

    private static void closeStream(Closeable stream) 
        if (stream != null) 
            try 
                stream.close();
             catch (Exception e) 
                // nothing
            
        
    


我们讨论的前提是EXE没有问题,单独在命令行调用EXE能正常执行。

上面这种写法,如果是调用输出比较少的EXE程序,是没有问题的。如果调用输出比较多的EXE程序,比如 ffmpeg ,会出现Java程序阻塞没有反应的问题。

正确写法

正确的做法应该是启动两个线程,分别接受EXE正常的输出流和报错的输出流。代码分两个类,分别是处理线程的 CmdInputStreamRunnable 和 CmdUtils。

CmdInputStreamRunnable.java


import java.io.*;

public class CmdInputStreamRunnable implements Runnable 
    private StringBuffer stringBuffer;
    private InputStream inputStream;

    public CmdInputStreamRunnable(StringBuffer stringBuffer, InputStream inputStream) 
        this.stringBuffer = stringBuffer;
        this.inputStream = inputStream;
    

    @Override
    public void run() 
        BufferedReader bufrIn = null;
        try 
            bufrIn = new BufferedReader(new InputStreamReader(inputStream, "UTF-8"));
            // 读取输出
            String line = null;
            while ((line = bufrIn.readLine()) != null) 
                stringBuffer.append(line).append('\\n');
            
         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         finally 
            try 
                if (null != bufrIn) 
                    bufrIn.close();
                
             catch (IOException e) 
                e.printStackTrace();
            
        
    

CmdUtils.java


import java.io.*;

public class CmdUtils 
    /**
     * 执行系统命令, 返回执行结果
     *
     * @param cmd 需要执行的命令
     * @param dir 执行命令的子进程的工作目录, null 表示和当前主进程工作目录相同
     */
    public static String execCmd(String cmd, File dir) 
        StringBuffer result = new StringBuffer();

        Process process = null;

        try 
            // 执行命令, 返回一个子进程对象(命令在子进程中执行)
            process = Runtime.getRuntime().exec(cmd, null, dir);

            CmdInputStreamRunnable processInput = new CmdInputStreamRunnable(result, process.getInputStream());
            CmdInputStreamRunnable processError = new CmdInputStreamRunnable(result, process.getErrorStream());

            new Thread(processInput).start();
            new Thread(processError).start();


            // 方法阻塞, 等待命令执行完成(成功会返回0)
            process.waitFor();

         catch (UnsupportedEncodingException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
         catch (InterruptedException e) 
            e.printStackTrace();
         finally 
            // 销毁子进程
            if (process != null) 
                process.destroy();
            
        
        // 返回执行结果
        return result.toString();
    



开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系

以上是关于168Java调用EXE程序出现阻塞问题的解决方法的主要内容,如果未能解决你的问题,请参考以下文章

168Java调用EXE并利用多线程接收EXE的输出流

解决waitfor()阻塞问题

Java 技术篇 - 通过exe4j打包后的程序运行过程中出现中文乱码问题解决

Java 中你怎样唤醒一个阻塞的线程?

使用java传参调用exe并且获取程序进度和返回结果的一种方法

java Future 阻塞