In the simple case that I am aware of, a process can only have one input stream, along with one output stream and one error stream. So if you don't have control over what App.main
does, or you don't want to modify it, then you could create multiple processes.
Imagine doing the following multiple times:
- Opening a CLI instance.
- Executing the runnable program you want (the one that calls
App.main
as far as I understand from your question), but with redirected input stream.
You can do this programmatically with java.lang.Process
.
Processes are heavier than threads (in terms of resource consumption), I know, but in case your scenario is simple (eg you need only a handful of processes in parallel) then you can try the following:
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
public class MultiSystemIn {
private static class ExecuteProcessRunnable implements Runnable {
private final String input;
private final List<String> command;
public ExecuteProcessRunnable(final List<String> command,
final String input) {
this.input = Objects.requireNonNull(input);
if (command == null || command.isEmpty())
throw new IllegalArgumentException("Empty command.");
this.command = new ArrayList<>(command);
}
@Override
public void run() {
final ProcessBuilder builder = new ProcessBuilder(command);
Process process = null;
try {
//Start the command:
process = builder.start();
//process.getOutputStream() is what the process will be reading as its own System.in...
try (final OutputStreamWriter osw = new OutputStreamWriter(process.getOutputStream(), StandardCharsets.UTF_8)) {
osw.write(input);
}
//process.getErrorStream() is what we can read as the corresponding System.err of the command.get(0) program...
try (final InputStreamReader isr = new InputStreamReader(process.getErrorStream(), StandardCharsets.UTF_8);
final BufferedReader br = new BufferedReader(isr)) {
for (String errline = br.readLine(); errline != null; errline = br.readLine())
System.out.println("Process " + command + " error stream says: " + errline);
}
}
catch (final IOException ioe) {
System.err.println("Process " + command + " failed with " + ioe);
}
finally {
if (process != null) {
process.destroyForcibly();
try {
process.waitFor();
}
catch (final InterruptedException ie) {
}
}
}
}
}
public static void main(final String[] args) {
for (int i = 0; i < 10; ++i) {
System.out.println("Starting process " + i + "...");
final Thread t = new Thread(new ExecuteProcessRunnable(
Arrays.asList("java", "App"),
"o\n611019\n2\n748943\n8\n \nq\n"
));
t.setDaemon(false);
t.start();
}
}
}
The above code also uses threads, where each one corresponds to managing one of the processes (eg relaying error stream, writing the standard input of the process and waiting for process termination).
By default a process created this way uses a pipe for its standard input stream, but pipes don't have unlimited buffers (you have to read from them at some point in order to be able to write to them again in the case their buffer gets full). This is the reason why I also used threads, because if one did the same without threads (ie just run a loop in main
creating and starting the Process
es and then writing to their streams) then in the case the input String
got too big for some reason then a pipe could block, not letting the other processes start.
You could do this also with redirecting the streams with files instead of writing manually to the stream of each process. There are also some other customizations one can do (just read the documentation of Process
and ProcessBuilder
).