1

I am trying to write a script that will run a .exe program 4 times with different parameters. I created one thread for each .exe run. Each thread will write an output file. My problem is that, it should write in parallel, but as you can you see on the screenshot below, the file write one after another. How should this be resolved?

enter image description here

Here's the main method:

public static void main (String args[]) {
    ExecutorService executor = Executors.newFixedThreadPool(4);
    executor.execute(new RunnableReader("myprogram.exe", param1, outputFile1));
    executor.execute(new RunnableReader("myprogram.exe", param2, outputFile2));
    executor.execute(new RunnableReader("myprogram.exe", param3, outputFile3));
    executor.execute(new RunnableReader("myprogram.exe", param4, outputFile4));
    executor.shutdown();
}

Here's the runnable class:

public class RunnableReader implements Runnable {
    private String program;
    private String param;
    String outputFile;

    public RunnableReader(String program, String param, String outputFile) {
        this.program = program;
        this.param = param;
        this.outputFile = outputFile;
    }

    @Override
    public void run() {
        try {
            ProcessBuilder pb = new ProcessBuilder(program, param);
            pb.redirectOutput(ProcessBuilder.Redirect.PIPE);
            pb.redirectErrorStream(true);
            Process proc = pb.start();
            InputStream stream = proc.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
            BufferedWriter writer = new BufferedWriter(new FileWriter(outputFile, true));
            for(String output; (output = reader.readLine()) != null) {
                writer.append(output);
                writer.append("\n");
            }
            writer.close();
            reader.close();
            stream.close();
  
        } catch(IOException e) {
            e.printStackTrace();
        }
    }
}
Shizuka Masuda
  • 89
  • 3
  • 13
  • 2
    I don't see anything in the Java code that should prevent parallel execution. Are you sure that the `myprogram.exe` can run in parallel? Maybe the program takes an exclusive lock on a file or a database. Have you tried opening 4 command prompt and execute it 4 times in parallel manually? – Andreas Aug 12 '20 at 21:03
  • That, and just because your timestamps are not at the same moment doesn't mean the exe's were not run concurrently. It may be possible they just take different amounts of time to run. There's a lot of time in between each file timestamp (30+ minutes in some of them). – SnakeDoc Aug 12 '20 at 21:05
  • 1
    To learn more, try inserting print statements in the Java code, e.g. a print statement after the `pb.start()` saying that the process has been started. If that print 4 times immediately, you know for sure it's not Java causing the issue. – Andreas Aug 12 '20 at 21:06
  • @SnakeDoc The fact the the "Date created" (file open time) exactly matches the "Date modified" (file close time) of another file strongly suggests that the 4 files were created serially. – Andreas Aug 12 '20 at 21:08
  • @Andreas Ah yes, good point, I missed that in the screenshot! Then it must be something external, such as a DB lock or something like you mentioned. – SnakeDoc Aug 12 '20 at 21:11
  • @Andreas Yes I tried it on CMD and the myprogram.exe can run in parallel. – Shizuka Masuda Aug 12 '20 at 21:16
  • "_Yes I tried it on CMD and the myprogram.exe can run in parallel._" – Why not try writing an actual script (e.g. batch or powershell) instead of a Java program? Then just execute the script and let it handle launching four instances of your executable in parallel. – Slaw Aug 12 '20 at 23:33

1 Answers1

0

I haven't been able to test this myself and I don't know if it actually causes the execution to block. But for what it's worth I thought I should point out that your reading of the process' InputStream might be unnecessary.

As stated by the Oracle docs ProcessBuilder.redirectOutput(ProcessBuilder.Redirect.PIPE) causes Process.getInputStream() to return the process' standard output.

With that in mind you could get rid of the entire for-loop and instead just do something like ProcessBuilder.redirectOutput(new File(outputFile)) so that your method instead looks like this

@Override
public void run() {
    try {
        ProcessBuilder pb = new ProcessBuilder(program, param);
        pb.redirectOutput(new File(outputFile));
        pb.redirectErrorStream(true);
        Process proc = pb.start();
    } catch(IOException e) {
        e.printStackTrace();
    }
Erik Karlstrand
  • 1,479
  • 9
  • 22