真的,你不能...唯一的方法是使用 thread.stop,同意“合作”方法(例如,偶尔检查 Thread.isInterrupted 或调用抛出 InterruptedException 的方法,例如 Thread.sleep()),或者以某种方式完全调用另一个 JVM 中的方法。
对于某些类型的测试,调用 stop() 是可以的,但它可能会损坏测试套件的状态,因此如果您想避免交互,则必须在每次调用 stop() 后重新启动 JVM效果。
有关如何实施协作方法的详细说明,请查看Sun's FAQ on the deprecated Thread methods。
对于这种方法在现实生活中的示例,Eclipse RCP's Job API's'IProgressMonitor' 对象允许某些管理服务(通过“取消”方法)向子进程发出信号,表明它们应该停止。当然,这依赖于定期实际检查 isCancelled 方法的方法,而他们经常无法做到这一点。
一种混合方法可能是用中断很好地询问线程,然后在几秒钟后用停止坚持。同样,您不应该在生产代码中使用 stop ,但在这种情况下它可能没问题,尤其是。如果您很快退出 JVM。
为了测试这种方法,我编写了一个简单的工具,它接受一个可运行文件并尝试执行它。随意评论/编辑。
public void testStop(Runnable r) {
Thread t = new Thread(r);
t.start();
try {
t.join(2000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
if (!t.isAlive()) {
System.err.println("Finished on time.");
return;
}
try {
t.interrupt();
t.join(2000);
if (!t.isAlive()) {
System.err.println("cooperative stop");
return;
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.err.println("non-cooperative stop");
StackTraceElement[] trace = Thread.getAllStackTraces().get(t);
if (null != trace) {
Throwable temp = new Throwable();
temp.setStackTrace(trace);
temp.printStackTrace();
}
t.stop();
System.err.println("stopped non-cooperative thread");
}
为了测试它,我编写了两个相互竞争的无限循环,一个是协作的,一个从不检查其线程的中断位。
public void cooperative() {
try {
for (;;) {
Thread.sleep(500);
}
} catch (InterruptedException e) {
System.err.println("cooperative() interrupted");
} finally {
System.err.println("cooperative() finally");
}
}
public void noncooperative() {
try {
for (;;) {
Thread.yield();
}
} finally {
System.err.println("noncooperative() finally");
}
}
最后,我编写了测试(JUnit 4)来练习它们:
@Test
public void testStopCooperative() {
testStop(new Runnable() {
@Override
public void run() {
cooperative();
}
});
}
@Test
public void testStopNoncooperative() {
testStop(new Runnable() {
@Override
public void run() {
noncooperative();
}
});
}
我以前从未使用过 Thread.stop(),所以我不知道它的操作。它的工作原理是从目标线程当前正在运行的任何地方抛出一个 ThreadDeath 对象。这扩展了错误。因此,虽然它并不总是干净地工作,但它通常会使简单的程序具有相当合理的程序状态。例如,调用任何 finally 块。如果你想成为一个真正的混蛋,你可以catch ThreadDeath(或错误),然后继续运行!
如果没有别的,这真的让我希望更多的代码遵循 IProgressMonitor 方法 - 向可能需要一段时间的方法添加另一个参数,并鼓励方法的实现者偶尔轮询 Monitor 对象看看用户是否希望系统放弃。将来我会尝试遵循这种模式,尤其是可能是交互式的方法。当然,你不一定事先知道会以这种方式使用哪些方法,但我猜这就是 Profiler 的用途。
至于“完全启动另一个 JVM”方法,这将需要更多的工作。我不知道是否有人编写了委托类加载器,或者是否包含在 JVM 中,但这种方法需要这样做。