终止正在运行本机代码的线程


问题内容

在我的应用程序中,我对一些本机代码进行了包装,这是通过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脚本中也可以),这仅是让您进行计算。您可以从主应用程序启动该过程,完成任务,然后启动看门狗。看门狗检测到超时-
强制杀死进程。

这是解决方案的唯一草案。如果您想提高性能(启动过程可能需要一些时间),则可以实现某种类型的进程池,等等。