问题
A very good article (When Runtime.exec() won't) says: The only possible time you would use exitValue() instead of waitFor() would be when you don't want your program to block waiting on an external process that may never complete. Instead of using the waitFor() method, I would prefer passing a boolean parameter called waitFor into the exitValue() method to determine whether or not the current thread should wait. A boolean would be more beneficial because exitValue() is a more appropriate name for this method, and it isn't necessary for two methods to perform the same function under different conditions. Such simple condition discrimination is the domain of an input parameter.
I have exactly same situation where my system call would start a process which will keep running until user decides to kill it. If I use '(process.waitFor() == 0)' it will block program there because process will not be completed. Author in article above suggest that exitValue() can be used with 'waitFor' parameter. Did anybody try it out ? Any example would be helpful.
Code:
// Start ProcessBuilder, 'str' contains a command
ProcessBuilder pbuilder = new ProcessBuilder(str);
pbuilder.directory(new File("/root/workspace/Project1"));
pbuilder.redirectErrorStream(true);
Process prcs = pbuilder.start();
AForm.execStatustext.append("\n=> Process is:" + prcs);
// Read output
StringBuilder out = new StringBuilder();
BufferedReader bfrd = new BufferedReader(new InputStreamReader(process.getInputStream()));
String current_line = null, previous_line = null;
while ((current_line = bfrd.readLine()) != null) {
if (!line.equals(previous_line)) {
previous_line = current_line;
out.append(current_line).append('\n');
//System.out.println(line);
}
}
//process.getInputStream().close();
// Send 'Enter' keystroke through BufferedWriter to get control back
BufferedWriter bfrout = new BufferedWriter(new OutputStreamWriter(prcs.getOutputStream()));
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
bfrout.write("\\r");
bfrout.newLine();
bfrout.flush();
//process.getOutputStream().close();*/
if (prcs.waitFor() == 0)
System.out.println("Commands executed successfully");
System.exit(0);
回答1:
Before using waitFor in main thread, create another thread (child) and construct logic for your termination cases in this new thread. For example, wait for 10 secs. If the condition is fulfilled, then interrupt the main thread from the child thread ant handle the following logic on your main thread.
The following code creates a child thread to invoke the process and the main thread does its work until the child finishes successfully.
import java.io.IOException;
public class TestExecution {
public boolean myProcessState = false;
class MyProcess implements Runnable {
public void run() {
//------
Process process;
try {
process = Runtime.getRuntime().exec("your command");
process.waitFor();
int processExitValue = process.exitValue();
if(processExitValue == 0) {
myProcessState = true;
}
} catch (IOException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void doMyWork() {
MyProcess myProcess = new MyProcess();
Thread myProcessExecuter = new Thread(myProcess);
myProcessExecuter.start();
while(!myProcessState) {
// do your job until the process exits with success
}
}
public static void main(String[] args) {
TestExecution testExecution = new TestExecution();
testExecution.doMyWork();
}
}
回答2:
This is a "rough" example of some library code I use to launch external processes.
Basically, this uses three threads. The first is used to execute the actually command and then wait till it exists.
The other two deal with the processes output and input streams. This makes these independent of each other prevents the ability for one to block the other.
The whole thing is then tied together with a listener that is notified when something happens.
The error handling could be better (as the fail condition is a little unclear as to what/who actually failed), but the basic concept is there...
This means you can launch the process and not care...(until you want to)
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TestBackgroundProcess {
public static void main(String[] args) {
new TestBackgroundProcess();
}
public TestBackgroundProcess() {
BackgroundProcess bp = new BackgroundProcess("java", "-jar", "dist/BackgroundProcess.jar");
bp.setListener(new ProcessListener() {
@Override
public void charRead(BackgroundProcess process, char value) {
}
@Override
public void lineRead(BackgroundProcess process, String text) {
System.out.println(text);
}
@Override
public void processFailed(BackgroundProcess process, Exception exp) {
System.out.println("Failed...");
exp.printStackTrace();
}
@Override
public void processCompleted(BackgroundProcess process) {
System.out.println("Completed - " + process.getExitValue());
}
});
System.out.println("Execute command...");
bp.start();
bp.send("dir");
bp.send("exit");
System.out.println("I'm not waiting here...");
}
public interface ProcessListener {
public void charRead(BackgroundProcess process, char value);
public void lineRead(BackgroundProcess process, String text);
public void processFailed(BackgroundProcess process, Exception exp);
public void processCompleted(BackgroundProcess process);
}
public class BackgroundProcess extends Thread {
private List<String> commands;
private File startIn;
private int exitValue;
private ProcessListener listener;
private OutputQueue outputQueue;
public BackgroundProcess(String... cmds) {
commands = new ArrayList<>(Arrays.asList(cmds));
outputQueue = new OutputQueue(this);
}
public void setStartIn(File startIn) {
this.startIn = startIn;
}
public File getStartIn() {
return startIn;
}
public int getExitValue() {
return exitValue;
}
public void setListener(ProcessListener listener) {
this.listener = listener;
}
public ProcessListener getListener() {
return listener;
}
@Override
public void run() {
ProcessBuilder pb = new ProcessBuilder(commands);
File startIn = getStartIn();
if (startIn != null) {
pb.directory(startIn);
}
pb.redirectError();
Process p;
try {
p = pb.start();
InputStreamConsumer isc = new InputStreamConsumer(p.getInputStream(), this, getListener());
outputQueue.init(p.getOutputStream(), getListener());
outputQueue.start();
p.waitFor();
isc.join();
outputQueue.terminate();
outputQueue.join();
ProcessListener listener = getListener();
if (listener != null) {
listener.processCompleted(this);
}
} catch (InterruptedException ex) {
ProcessListener listener = getListener();
if (listener != null) {
listener.processFailed(this, ex);
}
} catch (IOException ex) {
ProcessListener listener = getListener();
if (listener != null) {
listener.processFailed(this, ex);
}
}
}
public void send(String cmd) {
outputQueue.send(cmd);
}
}
public class OutputQueue extends Thread {
private List<String> cmds;
private OutputStream os;
private ProcessListener listener;
private BackgroundProcess backgroundProcess;
private ReentrantLock waitLock;
private Condition waitCon;
private boolean keepRunning = true;
public OutputQueue(BackgroundProcess bp) {
backgroundProcess = bp;
cmds = new ArrayList<>(25);
waitLock = new ReentrantLock();
waitCon = waitLock.newCondition();
}
public ProcessListener getListener() {
return listener;
}
public OutputStream getOutputStream() {
return os;
}
public BackgroundProcess getBackgroundProcess() {
return backgroundProcess;
}
public void init(OutputStream outputStream, ProcessListener listener) {
os = outputStream;
this.listener = listener;
}
public void send(String cmd) {
waitLock.lock();
try {
cmds.add(cmd);
waitCon.signalAll();
} finally {
waitLock.unlock();
}
}
public void terminate() {
waitLock.lock();
try {
cmds.clear();
keepRunning = false;
waitCon.signalAll();
} finally {
waitLock.unlock();
}
}
@Override
public void run() {
try {
Thread.sleep(500);
} catch (InterruptedException ex) {
}
BackgroundProcess backgroundProcess = getBackgroundProcess();
ProcessListener listener = getListener();
OutputStream outputStream = getOutputStream();
try {
while (keepRunning) {
while (cmds.isEmpty() && keepRunning) {
waitLock.lock();
try {
waitCon.await();
} catch (Exception exp) {
} finally {
waitLock.unlock();
}
}
if (!cmds.isEmpty()) {
waitLock.lock();
try {
while (!cmds.isEmpty()) {
String cmd = cmds.remove(0);
System.out.println("Send " + cmd);
outputStream.write(cmd.getBytes());
outputStream.write('\n');
outputStream.write('\r');
outputStream.flush();
}
} finally {
waitLock.unlock();
}
}
}
} catch (IOException ex) {
if (listener != null) {
listener.processFailed(backgroundProcess, ex);
}
}
}
}
public class InputStreamConsumer extends Thread {
private InputStream is;
private ProcessListener listener;
private BackgroundProcess backgroundProcess;
public InputStreamConsumer(InputStream is, BackgroundProcess backgroundProcess, ProcessListener listener) {
this.is = is;
this.listener = listener;
this.backgroundProcess = backgroundProcess;
start();
}
public ProcessListener getListener() {
return listener;
}
public BackgroundProcess getBackgroundProcess() {
return backgroundProcess;
}
@Override
public void run() {
BackgroundProcess backgroundProcess = getBackgroundProcess();
ProcessListener listener = getListener();
try {
StringBuilder sb = new StringBuilder(64);
int in = -1;
while ((in = is.read()) != -1) {
char value = (char) in;
if (listener != null) {
listener.charRead(backgroundProcess, value);
if (value == '\n' || value == '\r') {
if (sb.length() > 0) {
listener.lineRead(null, sb.toString());
sb.delete(0, sb.length());
}
} else {
sb.append(value);
}
}
}
} catch (IOException ex) {
listener.processFailed(backgroundProcess, ex);
}
}
}
}
回答3:
If I use '(process.waitFor() == 0)' it will block program there because process will not be completed.
No it won't. It will block the thread. That's why you have threads.
Author in article above suggest that exitValue() can be used with 'waitFor' parameter
No he doesn't. He is talking about how he would have designed it, if anybody had asked him. But they didn't, and he didn't.
Did anybody try it out ?
You can't. It doesn't exist.
来源:https://stackoverflow.com/questions/15260426/how-to-use-exitvalue-with-parameter