2014-02-20 5 views
13

Wenn ein in Java erstellter Prozess einen Unterprozess erstellt, aber dann zurückkehrt, hängt die JVM, jedoch ohne Prozess-ID.Warum hängt ein Java-Prozess von Gradle, wenn der Unterprozess noch offen ist?

Beispielanwendung unter (erfordert Windows und Java 7)

import java.io.File; 
import java.io.IOException; 
import java.lang.ProcessBuilder.Redirect; 
import java.nio.file.Files; 

public class SubProcessHang { 

    public static void main(String[] args) throws IOException, InterruptedException { 
     ProcessBuilder builder = new ProcessBuilder("cmd", "/c", "start", "notepad.exe"); 
     File output = Files.createTempFile("output", "txt").toFile(); 
     builder.redirectError(Redirect.to(output)); 
     builder.redirectOutput(Redirect.to(output)); 
     Process process = builder.start(); 
     process.waitFor(); 
     int exitValue = process.exitValue(); 
     System.out.println("Process exit value:: " + exitValue); 
     System.out.println("Output file length:: " + output.length()); 
     System.exit(exitValue); 
    } 
} 

Wenn die Anwendung ausgeführt wird, ist es drei Prozesse erzeugt: java -> cmd -> Notizblock cmd sofort zurück und Java Rufe System. exit (0), wodurch der Java-Prozess beendet wird. Aber Notepad ist immer noch da, und wenn es von Gradle (oder Eklipse für diese Angelegenheit) ausgeführt wird, hängt die JVM herum, bis dieser Prozess weggeht, und gibt seinen Rückgabewert nicht zurück.

So ist der Kinderprozess noch am Leben, aber der Elternprozess wurde teilweise getötet, ist aber für immer gestrandet.

Das build.gradle Skript dieses

apply plugin: 'java' 
apply plugin: 'application' 
mainClassName = "SubProcessHang" 

Ausführen ‚gradle run‘ und erhalten diese Ausgabe zu reproduzieren:

C:\HangDemo>gradlew run 
:compileJava 
:processResources UP-TO-DATE 
:classes 
:run 
Process exit value:: 0 
Output file length:: 0 
> Building 75% > :run 

Ich weiß es etwas damit zu tun haben müssen, wie die Java-Prozesse erstellt, aber ich habe keine Ahnung was zu tun ist.

Was kann ich tun, um die ID des laufenden Java-Prozesses zu erhalten und alle Unterprozesse in einem Shutdown-Hook zu beenden?

+0

Das sieht nicht so aus, als ob es wirklich etwas mit Gradle zu mir zu tun hat. Haben Sie versucht, Ihre Klasse einfach über eine Befehlszeile auszuführen? –

+1

Ja. Es funktioniert wie erwartet (wie ich sowieso erwarten würde) von der Befehlszeile. Es gibt den Exit-Status zurück und der Editor wird noch ausgeführt. Das Problem ist, dass Gradle das Automatisierungswerkzeug ist, das den hier modellierten Java-Prozess ausführt und niemals zurückkehrt. – w25r

Antwort

2

Die Dokumentation für Prozess sagen

standardmäßig die erstellt subprocess keinen eigenen Terminal oder Konsole hat. Alle seine Standard-I/O (d.h. stdin, stdout, stderr) Operationen werden an den übergeordneten Prozess umgeleitet werden, wo sie über die Datenströme zugegriffen werden kann, das Verfahren getOutputStream erhalten() verwendet, getInputStream() und getErrorStream(). Der übergeordnete Prozess verwendet diese Datenströme, um Eingaben in den Unterprozess zu leiten und Ausgaben daraus zu erhalten. Da einige native Plattformen nur eine begrenzte Puffergröße für Standardeingabe- und -ausgabeströme bereitstellen, kann ein Fehler beim sofortigen Schreiben des Eingabestreams oder beim Lesen des Ausgabestreams des Unterprozesses dazu führen, dass der Unterprozess blockiert oder sogar blockiert.

http://docs.oracle.com/javase/7/docs/api/java/lang/Process.html

Vielleicht stdout oder stderr Ihr Prozess ausgegeben zu schaffen. Versuchen Sie, den InputStream und den ErrorStream zu leeren.

+1

Ich verbinde die Ausgangsströme des tatsächlichen beleidigenden Prozesses. Das ist nicht das Problem. Im Beispiel hat notepad.exe keine Ausgabe. Es ist einfach so, dass es noch lebt. – w25r

+0

Was ist mit den Strömen des Prozesses, den Sie ausgeführt haben? In Ihrem Beispiel spreche ich über das Entleeren von p.getInputStream() und p.getErrorStream() –

+0

Ich habe das Beispiel geklärt, um zu zeigen, dass das nicht das Problem ist. Wenn das ein Problem ist, bleibt normalerweise process.waitFor() hängen, und die JVM-Threads bleiben am Leben. Beides passiert hier nicht. – w25r

1

Ich würde sagen, dass this answer könnte helfen mit dem Abrufen der Unterprozess-IDs und this one - mit dem Töten sie in Windows-Umgebung.

Hoffe, dass hilft!

+0

Das Problem damit ist, dass der Java-Prozess den CMD-Prozess startet, der den Notepad-Prozess startet ... aber dann verschwindet der CMD-Prozess. Welches ist die einzige Verbindung zwischen Java und Notizblock. Sie können also nicht alle Kinderprozesse beenden, weil sie nicht mehr von notepad.exe wissen. – w25r

Verwandte Themen