16

複数のスレッドを開始し、各 exec() を実行してから destroy() を実行中の Java プロセスにすると、プロセスの一部が破棄されず、プログラムの終了後も実行されます。問題を再現するコードを次に示します。開始するスレッドが多いほど、より多くのプロセスが存続することに気付きました。そして、destroy() の前にスリープするほど、生きているプロセスが少なくなります。(私は例として InfiniteLoop を使用しました。実行中のプロセスはすべてうまくいきます。)

EDIT : バグは Oracle に報告されており、回答を待っています。このテーマに関する知識/実験を自由に共有してください。

for(int i = 0; i < 100; i++)
{
  new Thread(new Runnable()
  {
    public void run()
    {
      try
      {
        Process p = Runtime.getRuntime().exec(new String[]{"java", "InfiniteLoop"});
        Thread.sleep(1);
        p.destroy();
      }catch(IOException | InterruptedException e){e.printStackTrace();}                    
    }
  }).start();
}
4

8 に答える 8

6

p.waitFor();p.destroy();に、

これにより、前のプロセスが確実に完了します。exec()コマンドがアクションを実行するよりも早く p.destroy コマンドが呼び出されると思います。だから駄目になる。

于 2013-08-15T17:20:36.097 に答える
2

これは単に、スレッドが destroy 呼び出しを実行する前に、メイン プログラムが終了し、関連するすべてのスレッドが開始されたプロセスを実行したままにするためです。これを確認するには、破棄の後に System.out 呼び出しを追加するだけで、実行されないことがわかります。これを克服するには、メイン メソッドの最後に Thread.sleep を追加します。孤立したプロセスはなくなります。以下は、プロセスを実行したままにしません。

public class ProcessTest {

public static final void main (String[] args) throws Exception {

    for(int i = 0; i < 100; i++) {
        new Thread(new Runnable()
            {
                public void run() {
                    try {
                        Process p = Runtime.getRuntime().exec(new String[]{"java", "InfiniteLoop"});
                        Thread.sleep(1);
                        p.destroy();
                        System.out.println("Destroyed");
                    }catch(IOException e) {
                        System.err.println("exception: " + e.getMessage());
                    } catch(InterruptedException e){
                        System.err.println("exception: " + e.getMessage());
                    }
                }
            }).start();
    }


    Thread.sleep(1000);

}

}

于 2013-08-19T12:00:49.057 に答える
1

linkによると、この呼び出しに応答して、オペレーティング システムによって個別のプロセスが生成されると思います。このプロセスには、Java プログラムとその中のスレッドから独立した存続期間があるため、プログラムが終了した後も実行し続けることが期待できます。自分のマシンで試してみたところ、期待どおりに動作するように見えました。

import java.io.*;

class Mp {
public static void main(String []args) {
    for(int i = 0; i < 100; i++) {
        new Thread(new Runnable() {
            public void run() {
                try {
                    System.out.println("1");
                    Process p = Runtime.getRuntime().exec
                        (new String[]{"notepad", ""});
                    System.out.println("2");
                    Thread.sleep(5);
                    System.out.println("3");
                    p.destroy();
                    System.out.println("4");
                }
                catch(IOException | InterruptedException e) {
                    e.printStackTrace();
                }                    
            }
        }).start();
    }
}
}
于 2013-08-07T21:18:27.487 に答える
0

非常によく似た問題がありdestroy()、単一のスレッドでも機能しないという問題が発生していました。

Process process = processBuilder(ForeverRunningMain.class).start()
long endTime = System.currentTimeMillis() + TIMEOUT_MS; 
while (System.currentTimeMillis() < endTime) {
    sleep(50);
}
process.destroy();

TIMEOUT_MSが低すぎる場合、プロセスは必ずしも破壊されませんでした。修正する前に追加を追加しましたsleep()destroy()(理由は説明していませんが):

Thread.sleep(300);
process.destroy();
于 2015-08-16T15:37:08.040 に答える
0

Runtime.exec がプロセスを開始するために新しいスレッドを開始してから、プロセス自体を破棄するように指示するまでの間に競合状態が発生します。

私は Linux マシンを使用しているので、UNIXProcess.class ファイルを使用して説明します。

Runtime.exec(...)新しいインスタンスを作成ProcessBuilderして起動し、UNIX マシンでは新しいUNIXProcessインスタンスを作成します。のコンストラクターにUNIXProcessは、バックグラウンド (フォークされた) スレッドで実際にプロセスを実行する次のコード ブロックがあります。

java.security.AccessController.doPrivileged(
            new java.security.PrivilegedAction() {
    public Object run() {
    Thread t = new Thread("process reaper") {
        public void run() {
                    try {
                        pid = forkAndExec(prog,
                      argBlock, argc,
                      envBlock, envc,
                      dir,
                      redirectErrorStream,
                      stdin_fd, stdout_fd, stderr_fd);
                    } catch (IOException e) {
                        gate.setException(e); /*remember to rethrow later*/
                        gate.exit();
                        return;
                    }
                    java.security.AccessController.doPrivileged(
                    new java.security.PrivilegedAction() {
                        public Object run() {
                        stdin_stream = new BufferedOutputStream(new
                                                FileOutputStream(stdin_fd));
                        stdout_stream = new BufferedInputStream(new
                                                FileInputStream(stdout_fd));
                        stderr_stream = new FileInputStream(stderr_fd);
                        return null;
                    }
                    });
                    gate.exit(); /* exit from constructor */
        int res = waitForProcessExit(pid);
        synchronized (UNIXProcess.this) {
            hasExited = true;
            exitcode = res;
            UNIXProcess.this.notifyAll();
        }
        }
    };
            t.setDaemon(true);
            t.start();
    return null;
    }
});

バックグラウンド スレッドpidが UNIX プロセス ID であるフィールドを設定することに注意してください。これはdestroy()、どのプロセスを強制終了するかを OS に伝えるために使用されます。

が呼び出されたときにこのバックグラウンド スレッドが実行されたことを確認する方法がないためdestroy()、プロセスが実行される前にプロセスを強制終了しようとするか、pid フィールドが設定される前にプロセスを強制終了しようとする場合があります。pid は初期化されていないため、0 です。したがって、destroy の呼び出しが早すぎると、kill -9 0

UNIXProcess には、destroy()これをほのめかしているコメントもありますが、プロセスが開始される前ではなく、プロセスがすでに終了した後にのみ破棄を呼び出すことを検討しています。

// There is a risk that pid will be recycled, causing us to
// kill the wrong process!  So we only terminate processes
// that appear to still be running.  Even with this check,
// there is an unavoidable race condition here, but the window
// is very small, and OSes try hard to not recycle pids too
// soon, so this is quite safe.

pid フィールドは volatile とさえマークされていないため、常に最新の値が表示されるとは限りません。

于 2013-08-15T20:28:28.363 に答える