2014-02-20 39 views
13

如果在java中创建的进程创建子进程,但之后返回,则JVM挂起,但没有进程ID。下面当子进程仍然打开时,为什么Java进程从Gradle挂起?

示例应用程序(需要Windows和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); 
    } 
} 

当应用程序运行时,它会创建三个过程: 爪哇 - > CMD - >记事本 CMD立即返回,Java调用系统。退出(0),这会杀死java进程。 但是记事本仍然存在,并且在从gradle(或者eclipse)运行时,JVM挂起,直到该进程消失,而不是返回它的返回值。

因此,孩子的过程仍然活着,但父母的过程已被部分杀害,但现在被永远搁浅。

的脚本的build.gradle重现此

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

执行“gradle这个运行”,并得到如下的输出:

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

我知道你肯定有事情做与Java进程如何创建,但我不知道该怎么做。

我能做些什么来获取正在运行的java进程的ID并在关闭钩子中查杀所有子进程?

+0

这看起来不像是对我的Gradle真的有用。你有没有尝试过从命令行运行你的课程? –

+1

是的。它按预期工作(正如我所料,无论如何)都是从命令行运行的。它返回退出状态,记事本仍在运行。问题在于gradle是运行这里建模的java进程的自动化工具,并且永远不会返回。 – w25r

回答

2

的过程中的文档说

默认情况下,创建的子进程没有自己的终端或控制台。其所有标准I/O(即stdin,stdout,stderr)操作都将被重定向到父进程,可以通过使用方法getOutputStream(),getInputStream()和getErrorStream()获取的流访问它们。父进程使用这些流将输入提供给子进程并从子进程获取输出。由于某些本地平台仅为标准输入和输出流提供有限的缓冲区大小,因此无法及时写入输入流或读取子流程的输出流可能导致子流程阻塞甚至死锁。

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

也许你的过程是创造stdout或stderr输出。尝试排空InputStream和ErrorStream。

+1

我正在管道实际违规流程的输出流。这不是问题。在这个例子中,notepad.exe没有输出。只是它还活着。 – w25r

+0

您执行的流程流程如何?在你的例子中,我正在讨论排干p.getInputStream()和p。getErrorStream() –

+0

我已经阐明了这个例子来证明这不是问题。当这是一个问题时,通常process.waitFor()是挂起的,并且JVM线程保持活动状态。这些都不在这里发生。 – w25r

1

我会说this answer可能有助于获得子进程ID和this one - 与杀死他们在Windows环境中。

希望有帮助!

+0

所以这个问题是java进程启动了启动记事本进程的cm​​d进程......但是随后cmd进程消失了。这是java和记事本之间的唯一联系。所以你甚至不能杀死所有的子进程,因为它不再知道notepad.exe。 – w25r