2

Javaプログラムを介してシェルスクリプトを実行するために、jframeで以下のコードを使用しますが、実行中のプロセスを停止する他のメソッドを実装すると、このメソッドが最初にブロックされ、プログラムを停止できません。したがって、新しいプロセスを強制終了する必要があります。問題は、プロセスのpidを取得する方法と、プロセスを強制終了するためにバックグラウンドに置く方法です。

public static void execShellCmd(String cmd) {
    try {

        testPath = getPath();

        System.out.println(cmd);
        Runtime runtime = Runtime.getRuntime();
        process = runtime.exec(new String[] { "sh",
                testPath + "/install.sh", cmd, "&" });
        int exitValue = process.waitFor();
        value = exitValue;
        System.out.println("exit value: " + exitValue);
        BufferedReader buf = new BufferedReader(new InputStreamReader(
                process.getInputStream()));
        String line = "";
        BufferedWriter bufferedWriter = null;
        while ((line = buf.readLine()) != null) {
            System.out.println("exec response: " + line);
            log = line;
            writeToFile(line);
        }
    } catch (Exception e) {
        System.out.println(e);
    }
}
4

2 に答える 2

2

このために、java.util.concurrentにある機能を使用できます。

UI を含む既存のクラスに、次のようなものを追加する必要があります。

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

ジョブを実行する主要なコンポーネントは、Runnable私が名前を付けたMyShellExecutorで、次のようになります。

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

    }
}

注: 時間のかかる操作を実行する場合は、UI スレッドで実行しないようにしてください。それはユーザーをブロックするだけで、優れたユーザー エクスペリエンスを提供しません。ユーザーを別のスレッドで待たせる可能性のあることを常に実行します。

推奨読書: Java Concurrency In Practice

于 2013-02-27T09:54:35.617 に答える
2

「他のメソッド」がProcess元のオブジェクトから返されたオブジェクトにアクセスexecできる場合は、呼び出しprocess.destroy()て実行中のプロセスを強制終了できます。ネイティブ PID を知る必要はありません。SwingWorkerを使用した多かれ少なかれ完全な例を次に示します (いくつかの try/catch ブロックを忘れている可能性があります)。

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

execShellCmdstopProcessがイベント ディスパッチ スレッドからのみ呼び出される限り、runningProcessフィールドへのすべてのアクセスが同じスレッドで発生するため、同期は必要ありません (要点は、ワーカー スレッドSwingWorkerで発生しますが、EDT で実行されます)。doInBackgrounddone

于 2013-02-27T09:45:34.623 に答える