Java Process與Runtime()的使用及調用cmd命令阻塞的解決方案

Java Process與Runtime()使用

java調用cmd執行bat文件有時會出現卡死的現象,當時感覺很迷惑,後來查資料,本來一般都是這樣來調用程序並獲取進程的輸出流的,但是我在windows上執行這樣的調用的時候卻總是在while那裡被堵塞瞭,結果造成ffmpeg程序在執行瞭一會後不再執行,這裡從官方的參考文檔中我們可以看到這是由於緩沖區的問題,由於java進程沒有清空ffmpeg程序寫到緩沖區的內容,結果導致ffmpeg程序一直在等待。

在網上也查找瞭很多這樣的問題,不過說的都是使用單獨的線程來進行控制,我也嘗試過很多網是所說的方法,可一直沒起什麼作用。

一直認為是getInputStream的緩沖區沒有被清空,不過問題確實是緩沖區的內容沒有被清空,但不是getInputStream的,而是getErrorStream的緩沖區,這樣問題就得到解決瞭。

所以我們在遇到java調用外部程序而導致線程阻塞的時候,可以考慮使用兩個線程來同時清空process獲取的兩個輸入流,如下這段程序:

 public String excuteBatFile(String file, boolean isCloseWindow) 
    {
        String cmdCommand = null;
        String res = null;
        if(isCloseWindow) 
        {
            cmdCommand = "cmd.exe /c " + file;
        }else 
        {
            cmdCommand = "cmd.exe /k " + file;
        }
        StringBuilder stringBuilder = new StringBuilder();
        Process process = null;
        try {
            process = Runtime.getRuntime().exec(cmdCommand);
            final InputStream is1 = process.getInputStream();
            new Thread(new Runnable() {
                public void run() {
                    BufferedReader bufferedReader = null;
                    String line = null;
                    try {
                        bufferedReader = new BufferedReader(
                                new InputStreamReader(is1, "GBK"));
                        while((line=bufferedReader.readLine()) != null) 
                        {
                            stringBuilder.append(line+"\n");
                        }
                        is1.close();
                    } catch (Exception e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }).start(); // 啟動單獨的線程來清空p.getInputStream()的緩沖區
            InputStream is2 = process.getErrorStream();
            BufferedReader br2 = new BufferedReader(new InputStreamReader(is2)); 
            StringBuilder buf = new StringBuilder(); // 保存輸出結果流
            String line2 = null;
            while((line2 = br2.readLine()) != null) buf.append(line2); // 
            log.info("----res:----" + stringBuilder + "&" + buf);
            return stringBuilder + "&" + buf;
        } catch (Exception e) {
            e.printStackTrace();
            return e.toString();
        }
    }

通過這樣我們使用一個線程來讀取process.getInputStream()的輸出流,使用另外一個線程來獲取process.getErrorStream()的輸出流,這樣我們就可以保證緩沖區得到及時的清空而不擔心線程被阻塞瞭。

當然根據需要你也可以保留process.getInputStream()流中的內容,這個就看調用的程序的處理瞭。

java Process執行cmd命令流阻塞處理

代碼如下:

public static void runCmd() {
        Process process = null;
        BufferedReader bufferedReader = null;
        try {
            Logger.getLogger(SystemService.class).info("============= 開始重啟機器 =============");
            process = Runtime.getRuntime().exec("cmd.exe /c shutdown -r -f -t 0");
            bufferedReader = new BufferedReader(new InputStreamReader(new BufferedInputStream(process.getInputStream()), Charset.forName("GB2312")));
            // 開啟線程讀取錯誤輸出,避免阻塞
            new StreamInformatonThread(process.getErrorStream(), "error").start();
            String outStr;
            while ((outStr = bufferedReader.readLine()) != null) {
                Logger.getLogger(SystemService.class).info("readLine -------> : " + outStr);
            }
            Logger.getLogger(SystemService.class).info("============= 重啟機器完成 =============");
        } catch (IOException e) {
            Logger.getLogger(SystemService.class).error("============= 重啟機器失敗 =============");
            e.printStackTrace();
        } finally {
            if (process != null) {
                process.destroy();
            }
            if (bufferedReader != null) {
                try {
                    bufferedReader.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
import java.io.*;
/**
 * @Description:流阻塞處理
 * @Author: zhangwenchao
 * @Date: 2019/7/9 11:35
 */
public class StreamInformatonThread extends Thread {
    private InputStream is;
    private String str;
    private Logger logger = Logger.getLogger(StreamInformatonThread.class);
    public StreamInformatonThread(InputStream is, String str) {
        this.is = is;
        this.str = str;
    }
    public void run() {
        BufferedReader out = null;
        try {
            out = new BufferedReader(new InputStreamReader(is, "gbk"));
            String line;
            while ((line = out.readLine()) != null) {
                if (str.equals("error")) {
                    logger.info("ErrorStream --------> :" +line);
                } else {
                    logger.info("outLine ---------> :" + line);
                }
            }
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (out != null) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。