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 とさえマークされていないため、常に最新の値が表示されるとは限りません。