我有一个从进程读取输出的 gobbler。
有一种情况是我们使用它的 PID 和外部 Windows taskkill 命令以编程方式终止进程。
它是一个 16 位 DOS 进程
我们 taskkill 因为它是一个 DOS 16 位进程,而 Process.destroyForcibly() 不适用于它,因为它位于 ntvdm 子系统中,最好的方法是获取 PID 并使用 'taskkill /T /F' 确实可以杀死它和任何孩子。
通常,我们的 DOS 16 位(或 32 位)进程没有问题。这个有一些文件锁。尤其重要的是,我们确保让操作系统释放锁是死的。
我们在杀戮之前和之后关闭所有流
在调用 taskkill 之前,我们尝试刷新和关闭 executor 中的所有流:in,out,err。调用 taskkill 后,我们通过重新关闭所有流来验证它们是否已关闭。
杀死后我们在所有 gobbler 上调用 Thread.interrupt()
现在,在操作系统中也确认了 kill 成功后,gobbler 仍在运行,它不响应 Thread.interrupt()。
我们甚至做了最后的 Thread.stop(喘气!)
此外,我们在它上面调用了 Thread.stop() ,它仍然在读取阶段等待......
因此,我们似乎无法停止 Processes 流上的 std-out 和 std-in gobbler。
我们知道 Thread.stop() 已被弃用。为了安全起见,我们捕获 ThreadDeath,然后清理所有监视器,然后重新抛出 ThreadDeath。然而, ThreadDeath 实际上从来没有被抛出,线程只是继续等待 inputStream.read .. 所以 Thread.stop 在这种情况下被弃用是一个有争议的问题......因为它没有做任何事情。就这样没有人会激怒我,这样我就可以问心无愧,我们已经从生产代码中删除了 Thread.stop() 。
线程不会中断我并不感到惊讶,因为这只发生在某些 InputStreams 上,并且并非所有读取都是不可破坏的。但令我惊讶的是,调用 Thread.stop 时 Thread 不会停止。
线程跟踪显示
线程跟踪显示 main-in 和 main-er(进程的两个输出)仍然在运行,即使在流关闭、线程被中断并且最后一次 Thread.stop 被调用之后。
任务已经死了,那么为什么要关心空闲的被阻塞的gobblers呢?
并不是我们在乎狼吞虎咽不会退出。但是我们讨厌运行的线程只会堆积并阻塞系统。这个特定的进程由网络服务器调用,然后......它可能相当于数百个处于阻塞状态的空闲线程在死进程上......
我们已经尝试以两种方式启动该过程,没有区别......
run(working, "cmd", "/c", "start", "/B", "/W", "/SEPARATE", "C:\\workspace\\dotest.exe");
run(working, "cmd", "/c", "C:\\workspace\\dotest.exe");
狼吞虎咽的读法是这样的:
try (final InputStream is = inputStream instanceof BufferedInputStream
? inputStream : new BufferedInputStream(inputStream, 1024 * 64);
final BufferedReader br = new BufferedReader(new InputStreamReader(is, charset))) {
String line;
while ((line = br.readLine()) != null) {
lineCount++;
lines.add(line);
if (Thread.interrupted()) {
Thread.currentThread().interrupt();
throw new InterruptedException();
}
}
eofFound = true;
}
我们的销毁器在 taskkill 之后在 gobbler 线程上调用它:
int timeLimit = 500;
t.interrupt();
try {
t.join(timeLimit);
if (t.isAlive()) {
t.stop();
// we knows it's deprecated but we catch ThreadDeath
// then clean any monitors and then rethrow ThreadDeath
// But ThreadDeath never in fact gets thrown and the thread
// just keeps on waiting on inputStream.read ..
logger.warn("Thread stopped because it did not interrupt within {}ms: {}", timeLimit, t);
if (t.isAlive()) {
logger.warn("But thread is still alive! {}", t);
}
}
} catch (InterruptedException ie) {
logger.info("Interrupted exception while waiting on join({}) with {}", timeLimit, t, ie);
}
这是日志输出的片段:
59.841 [main] INFO Destroying process '5952'
04.863 [main] WARN Timeout waiting for 'Close java.io.BufferedInputStream@193932a' to finish
09.865 [main] WARN Timeout waiting for 'Close java.io.FileInputStream@159f197' to finish
09.941 [main] DEBUG Executing [taskkill, /F, /PID, 5952].
10.243 [Thread-1] DEBUG SUCCESS: The process with PID 5952 has been terminated.
10.249 [main] DEBUG java.lang.ProcessImpl@620197 stopped with exit code 0
10.638 [main] INFO Destroyed WindowsProcess(5952) forcefully in 738 ms.
11.188 [main] WARN Thread stop called because it did not interrupt within 500ms: Thread[main-in,5,main]
11.188 [main] WARN But thread is still alive! Thread[main-in,5,main]
11.689 [main] WARN Thread stop because it did not interrupt within 500ms: Thread[main-err,5,main]
11.689 [main] WARN But thread is still alive! Thread[main-err,5,main]
注意:在调用 taskkill 之前,进程 std-out 和 std-err 不会关闭。但是它们在 taskkill 之后手动关闭(因为成功而未显示在日志中)。