Java运行异步进程

8
我正在尝试运行一个异步进程,我不希望程序等待直到这些进程执行完毕。我发现了这个问题:如何在Java程序内部异步运行shell脚本,但它没有我要找的答案。
我的做法很简单,我只是在运行bash进程后,不想让Java程序等待它完成。这是我的做法:
public void runCommandLine(String directory) throws IOException {
    Thread commandLineThread = new Thread(() -> {
        try {
            ProcessBuilder processBuilder = new ProcessBuilder(
                    "/bin/bash");
            processBuilder.directory(new File(directory));
            Process process = processBuilder.start();
            try (OutputStreamWriter osw = new OutputStreamWriter(process.getOutputStream())) {
                osw.write(command);
            }
            printStream(process.getErrorStream(), true);
            printStream(process.getInputStream(), true);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    });
    commandLineThread.start();
    System.out.println("Task Dispatched");
}

我在主方法的末尾再次打印输出,所以我获得了以下输出结果:
Task Dispatched
Task Dispatched
End of psvm

然而,由于这两个进程没有终止,程序并未终止。

我该如何解决这个问题?

3个回答

5

你需要将线程设置为守护线程。在启动之前使用setDaemon(true)方法。

 commandLineThread.setDaemon(true);

守护线程是一种不会阻止JVM退出的线程。有关守护线程的更多信息,请参见此问题:Java中的守护线程是什么? 编辑:
通过您的评论判断,您需要即使在JVM即将退出时也能运行命令。我假设 command 变量包含要运行的脚本? 您可以进行两个更改,以使程序表现出我认为您想要的行为。
  1. 使用 -c 启动bash以执行您的命令,然后您就不必将内容发送到输出流。
  2. 在启动等待输出的线程之前启动进程。
生成的代码如下:
public void runCommandLine(String directory) throws IOException {
    ProcessBuilder processBuilder = new ProcessBuilder(
                    "/bin/bash -c " + command);
    processBuilder.directory(new File(directory));
    Process process = processBuilder.start();
    Thread commandLineThread = new Thread(() -> {
        try {
            printStream(process.getErrorStream(), true);
            printStream(process.getInputStream(), true);
        } catch (IOException ex) {
            ex.printStackTrace();
        }
    });
    commandLineThread.setDaemon(true);
    commandLineThread.start();
    System.out.println("Task Dispatched");
}

好的,当我使用它时,程序终止了,但命令没有运行。也许它终止得太快以至于无法运行该进程? - Sarp Kaya
很可能。尝试在主线程中启动命令,只在单独的线程中处理输出。 - K Erlandsson
我不太确定问题出在哪里,但是如果我将printStreams注释掉,那么它仍然无法正常工作,如果我将它们作为单独的线程运行,并在它们上使用setDaemon也不起作用。我猜测问题在于Bash没有足够的时间来实际运行它。我的解决方法是,在其中添加了Thread.sleep()并设置了2000毫秒的延迟,现在它可以正常工作了。我知道这听起来像一种不好的做法,但我真的不知道如何解决... - Sarp Kaya
@SarpKaya,请检查我的编辑,看看那是否是你真正想要的。 - K Erlandsson

0

您正在读取进程的输出流,这就是您的Java程序无法退出的原因:

        printStream(process.getErrorStream(), true);
        printStream(process.getInputStream(), true);

你的流读取会阻塞你的代码。

你可能想将启动的进程输出重定向到日志文件中,稍后再读取它。


如果我将其注释掉,那么我就可以看到它不会运行。 - Sarp Kaya
不要将其注释掉,你可以 - Vijay Kumar

0
           Thread commandLineThread = new Thread(() -> {
            try {
                BufferedReader br=new BufferedReader(
                        new InputStreamReader(
                                process.getInputStream()));
                String line;

                while((line=br.readLine())!=null){
                    System.out.println(line);
                }
            } catch (IOException ex) {
                ex.printStackTrace();
            }
        });
        commandLineThread.setDaemon(true);
        commandLineThread.start();

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接