how to kill runtime.exec?

和自甴很熟 提交于 2019-12-01 09:38:15

For this, you can employ the features present in java.util.concurrent

In your existing class containing your UI, you have to add something similar to this:

//class variable to store the future of your task
private Future<?> taskFuture = null;

//to be called from button "Start" action handler
public void actionStart() {

  //don't double start, if there is one already running
  if(taskFuture == null || taskFuture.isDone()) {

    //create the new runnable instance, with the proper commands to execute
    MyShellExecutor ex = new MyShellExecutor(new String[] { "sh",testPath + "/install.sh", cmd, "&" });

    //we only need one additional Thread now, but this part can be tailored to fit different needs
    ExecutorService newThreadExecutor = Executors.newSingleThreadExecutor();

    //start the execution of the task, which will start execution of the shell command
    taskFuture = newThreadExecutor.submit(ex);
  }
}

//to be called from button "Stop" action handler
public void actionStop() {
  //if not already done, or cancelled, cancel it
  if(taskFuture !=null && !taskFuture.isDone()) {
    taskFuture.cancel(true);
  }
}

The main component doing the job is the Runnable which I named MyShellExecutor, and looks like this:

public class MyShellExecutor implements Runnable {

    //stores the command to be executed
    private final String[] toExecute;

    public MyShellExecutor(String[] toExecute) {
        this.toExecute=toExecute;
    }

    public void run() {
        Runtime runtime = Runtime.getRuntime();
        Process process = null;

        try {
            process = runtime.exec(toExecute);

            int exitValue = process.waitFor();
            System.out.println("exit value: " + exitValue);
            BufferedReader buf = new BufferedReader(new InputStreamReader(process.getInputStream()));

            String line = "";
            while ((line = buf.readLine()) != null) {
                System.out.println("exec response: " + line);
                //do whatever you need to do
            }

        } catch (InterruptedException e) {
            //thread was interrupted.
            if(process!=null) { process.destroy(); }
            //reset interrupted flag
            Thread.currentThread().interrupt();

        } catch (Exception e) {
            //an other error occurred
            if(process!=null) { process.destroy(); }
        }

    }
}

Note: when running time consuming operations, be sure not to do it on the UI thread. That just blocks the user, and doesn't provide a nice user experience. Always do anzthing that might make the user wait on a different Thread.

Recommended reading: Java Concurrency In Practice

If your "other method" has access to the Process object returned by the original exec then you can simply call process.destroy() to kill the running process. You don't need to know the native PID. Here's a more-or-less complete example using SwingWorker (there may be a few try/catch blocks I've forgotten):

private Process runningProcess = null;

public static void execShellCmd(String cmd) {
    if(runningProcess != null) {
        // print some suitable warning about process already running
        return;
    }
    try {

        testPath = getPath();

        System.out.println(cmd);
        Runtime runtime = Runtime.getRuntime();
        runningProcess = runtime.exec(new String[] { "sh",
                testPath + "/install.sh", cmd });

        new SwingWorker<Integer, Void>() {
            protected Integer doInBackground() {
                // read process output first
                BufferedReader buf = new BufferedReader(new InputStreamReader(
                    process.getInputStream()));
                String line = "";
                while ((line = buf.readLine()) != null) {
                    System.out.println("exec response: " + line);
                    log = line;
                    writeToFile(line);
                }

                // only once we've run out of output can we call waitFor
                while(true) {
                    try {
                        return runningProcess.waitFor();
                    } catch(InterruptedException e) {
                        // do nothing, wait again
                    } finally {
                        Thread.interrupted();
                    }
                }
                return -1;
            }

            protected void done() {
                runningProcess = null;
            }
        }.execute();
    }
}

public static void stopProcess() {
    if(runningProcess != null) {
        runningProcess.destroy();
    }
}

As long as execShellCmd and stopProcess are only called from the event dispatch thread then no synchronization is necessary, as all accesses to the runningProcess field happen on the same thread (the point of SwingWorker is that doInBackground happens on a worker thread but done is run on the EDT).

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!