爲什麼父進程不處理標準輸入輸出子進程會掛起(Java)?

最近寫Java的多進程程序時遇到一個奇怪的問題,發現程序運行一段時間以後會自動掛起。按道理來說子進程和父進程之間是沒有太大的關係的,父進程只是用於開啓一個新的子進程,之後就沒怎麼聯繫了。最後查到了stackoverflow上面的一片帖子,寫的不錯。http://stackoverflow.com/questions/16983372/why-does-process-hang-if-the-parent-does-not-consume-stdout-stderr-in-java

Java doesn’t do anything in this area. It just uses OS services to create the pipes.

All Unix like OSs and Windows behave the same in this regard: A pipe with a 4K is created between parent and child. When that pipe is full (because one side isn’t reading), the writing process blocks.

This is the standard since the inception of pipes. There is not much Java can do.

What you can argue is that the process API in Java is clumsy and doesn’t have good defaults like simply connecting child streams to the same stdin/stdout as the parent unless the developer overrides them with something specific.

I think there are two reasons for the current API. First of all, the Java developers (i.e. the guys at Sun/Oracle) know exactly how the process API works and what you need to do. They know so much that it didn’t occur to them that the API could be confusing.

The second reason is that there is no good default that will work for the majority. You can’t really connect stdin of the parent and the child; if you type something on the console, to which process should the input go?

Similarly, if you connect stdout, the output will go somewhere. If you have a web app, there might be no console or the output might go somewhere where no one will expect it.

You can’t even throw an exception when the pipe is full since that can happen during the normal operation as well.

總結一下就是這個問題可能跟Java關係不大,應該是操作系統對pipe處理的問題。因爲父子進程之間通信可以利用匿名管道,但是如果子進程往管道里面的數據“塞”滿了,父進程卻“遲遲”不取,那麼操作系統就會hang子進程,不讓它接着寫數據了。所以這時候子進程就會沒反應了。掛起了嘛。

原因找到了,解決這個問題就很簡單了,直接開啓一個線程不停地去讀取匿名管道里面的數據即可。

String javaHome = System.getProperty("java.home");
String javaBin = javaHome + File.separator + "bin" + File.separator + "java";
String classpath = System.getProperty("java.class.path");
String className = klass.getCanonicalName();

ProcessBuilder builder = new ProcessBuilder(javaBin, "-cp", classpath, className, String.valueOf(mode));
builder.redirectErrorStream(true);
process = builder.start();
//      process.waitFor();

InputStream myIS = process.getInputStream();

new Thread(){
    public void run() {
        if(process == null)
            return ;
        InputStream is = process.getInputStream();
        InputStreamReader isr = new InputStreamReader(is);
        BufferedReader br = new BufferedReader(isr);
        String line;
        try {
            while ((line = br.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        System.out.println("Game closed!");
    }  
}.start();
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章