终止正在运行本机代码的线程
问题内容:
在我的应用程序中,我对一些本机代码进行了包装,这是通过JNI桥调用的。此本地代码需要在单独的线程中执行(并行处理)。但是,问题在于代码有时“挂起”,因此线程需要“强制”终止。不幸的是,我还没有找到任何“微妙的”方法来这样做:一般建议是告诉线程中的代码正常退出,但是我无法使用此本地代码(以上均为第三方代码)来执行此操作。
我使用Java Concurrent API进行任务提交:
Future<Integer> processFuture = taskExecutor.submit(callable);
try {
result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue();
}
catch (TimeoutException e) {
// How to kill the thread here?
throw new ExecutionTimeoutException("Execution timed out (max " + this.executionTimeout / 60 + "min)");
}
catch (...) {
... exception handling for other cases
}
Future#cancel()
只会中断线程,但不会终止线程。因此,我使用了以下技巧:
class DestroyableCallable implements Callable<Integer> {
private Thread workerThread;
@Override
public Integer call() {
workerThread = Thread.currentThread();
return Integer.valueOf(JniBridge.process(...));
}
public void stopWorkerThread() {
if (workerThread != null) {
workerThread.stop();
}
}
}
DestroyableCallable callable = new DestroyableCallable();
Future<Integer> processFuture = taskExecutor.submit(callable);
try {
result = processFuture.get(this.executionTimeout, TimeUnit.SECONDS).intValue();
}
catch (TimeoutException e) {
processFuture.cancel(true);
// Dirty:
callable.stopWorkerThread();
ThreadPoolTaskExecutor threadPoolTaskExecutor = (ThreadPoolTaskExecutor) taskExecutor;
logger.debug("poolSize: " + threadPoolTaskExecutor.getPoolSize() + ", maxPoolSize:"
+ threadPoolTaskExecutor.getMaxPoolSize() + ", activeCount:"
+ threadPoolTaskExecutor.getActiveCount());
}
throw new ...;
}
catch (...) {
... exception handling for other cases
}
与此代码有关的问题/问题:
- 通常这样做是否正确?还有其他更优雅的选择吗?
activeCount
任务执行程序上的操作没有减少,因此任务执行程序仍然“认为”线程正在运行- 我不得不在方法上添加
workerThread != null
检查stopWorkerThread()
,因为这个变量null
在某些情况下是存在的。我不明白这些情况是什么…
笔记:
- 本机代码不占用文件描述符(套接字)。一切都作为数据块传递给它,并以相同的方式返回。
- 本机代码占用大量CPU。即使保证终止,也可能需要很长时间。
赏金编辑 :重新访问本机代码的方法/建议很明确,请不要在答复中提供。我需要纯Java解决方案/解决方法。
问题答案:
Java具有用于强制终止线程的纯选项。它是仅适用于Thread.stop()的古老且已弃用的(AFAIK)。而且 没有 安全
终止线程的选项(这就是为什么不建议使用.stop(),并且甚至不允许JVM实现者实现的原因)。
原因是应用程序内的所有线程 共享 内存和资源-
因此,如果您在任意点强制终止线程,则无法确定哪个终止线程不会使某些共享内存/资源处于不一致状态。而且,您甚至不能(通常)假设 哪些
资源(可能)是脏的(因为您不知道确切的线程停止位置)。
因此,如果您希望应用程序的某些线程能够被中断,则唯一的解决方案是在设计阶段提供“保存点”的某些表示法,即目标线程代码中的位置,保证不会改变共享的位置状态,因此线程在此处退出是安全的。这正是javadocs
Thread.stop()所告诉的:安全地中断线程的唯一方法是设计线程的代码,以便它本身可以响应某种中断请求。某种标志,由线程不时检查。
我试图告诉您:您无法完成有关使用Java线程/并发的要求。我可能建议您的方法(在此处提供)是在单独的过程中完成工作。强制终止进程比线程安全得多,因为1)进程彼此之间的间隔更大,并且2)OS在进程终止后会处理许多清理工作。杀死进程并不完全安全,因为存在某种类型的资源(例如文件),默认情况下OS不会清除它们,但就您而言,这似乎是安全的。
因此,您可以设计一个单独的小应用程序(甚至可以在Java中使用-
如果您的第三方lib不提供其他绑定,甚至在shell脚本中也可以),这仅是让您进行计算。您可以从主应用程序启动该过程,完成任务,然后启动看门狗。看门狗检测到超时-
强制杀死进程。
这是解决方案的唯一草案。如果您想提高性能(启动过程可能需要一些时间),则可以实现某种类型的进程池,等等。