8

I am trying to run an async process and I do not want the program to wait until the end of these processes executions. I found this question how to run shell script asynchronously from within Java program but it doesn't have the answer that I am looking for.

What I am doing is I am simply running bash processes and after I run it, I do not want the Java program to wait until it's finished. This is what I have done:

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");
}

I also put another print out at the end of the main method so I get this output:

Task Dispatched
Task Dispatched
End of psvm

However the program does not terminate as these two processes have not terminated.

How can I solve this issue?

Community
  • 1
  • 1
Sarp Kaya
  • 3,686
  • 21
  • 64
  • 103

3 Answers3

5

You need to make your thread a daemon thread. Use setDaemon(true) before starting it.

 commandLineThread.setDaemon(true);

A daemon thread is a thread that does not prevent the JVM from exiting. See this question: What is Daemon thread in Java? for more information about daemon threads.

Edit:

By judging from your comments you need the command to run even though the JVM is about to exit. I assume the command variable contains the script you want to run? You could make two changes to make the program behave as I think you want.

  1. Start bash with -c to execute your command, then you do not have to send things to an output stream.
  2. Start the process before starting your thread that waits for the output.

The resulting code would look something like:

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");
}
Community
  • 1
  • 1
K Erlandsson
  • 13,408
  • 6
  • 51
  • 67
  • Okay, when I use this, program terminates but the command is not run. Perhaps it terminates too quickly to actually run the process? – Sarp Kaya Jun 08 '15 at 10:52
  • Probably. Try starting the command in the main thread and only handle the output in a separate thread. – K Erlandsson Jun 08 '15 at 10:54
  • I am not too sure what is the problem but if I comment the printStreams out, then it still does not work, again if I run them as separate threads and use setDaemon on them it doesn't work either. I am guessing that the issue is bash does not get enough time to actually run it. What I did is, I added `Thread.sleep()` with 2000 ms on it and it works fine now. I know it sounds like a bad practise, but I don't really know how to fix that... – Sarp Kaya Jun 08 '15 at 11:56
  • @SarpKaya check my edit to see if that is what you really want. – K Erlandsson Jun 08 '15 at 12:51
0

You are reading the output streams of your process, and thats the reason your java program does not exit:

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

Your stream reading will keep blocking your code.

You may like to redirect output of your launched process to a log file and read that later.

Juned Ahsan
  • 67,789
  • 12
  • 98
  • 136
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();
Vijay Kumar
  • 2,439
  • 2
  • 32
  • 51