最佳实践:立即停止线程

4

我有上下文,可以用来产生预测结果。基于上下文制定预测算法不是我的问题的一部分。但它需要花费大量时间,所以我将其从主线程中移出。任何时候上下文都可能改变。当这种情况发生时,应用程序应重新启动具有新输入的预测生成。与旧上下文相关的预测将变得无效,因此必须中断其计算。

目前,我使用以下代码:

public class BackgroundTasksManager {

public static final BackgroundTasksManager shared = new BackgroundTasksManager();

private ExecutorService conceptResultsExecutor = null;

private BackgroundTasksManager() { }

public void startConceptResultsCalculation(ConceptResultsCalculationInput input,
                                           Consumer<ConceptResultsCalculationOutput> callback) {
    ThreadGroup callbackGroup = Thread.currentThread().getThreadGroup();
    if (conceptResultsExecutor != null)
        conceptResultsExecutor.shutdownNow();

    conceptResultsExecutor = Executors.newSingleThreadExecutor();
    conceptResultsExecutor.submit(() -> {
        ConceptResultsCalculationOutput output = ConceptResultsCombinator.possbileResults(input);
        Thread callbackThread = new Thread(callbackGroup, () -> callback.accept(output));
        callbackThread.start();
    });
}

那么,当我需要开始预测计算时,我会关闭旧的Executor并创建一个新的。这种解决方案可以工作,但它是否有效或者隐藏了一些危险的细节呢?


Java平台中有一种单一的标准机制来中断线程,即中断标志。而线程应该自行检查标志。任何长时间/阻塞的操作都应该检查这个标志。例如,如果你有一个长时间运行的循环,应该定期检查interrupted() / isInterrupted()标志。顺便说一下,要中断类似socket.read()这样的阻塞IO操作,更好的方法是在另一个线程中关闭socket以立即从read()中退出并抛出IOException异常。 - AnatolyG
  1. 没有必要使用合成的volatile标志来停止线程计算,但这可能是线程即将被用户停止的迹象,因此将标志设置为true并在run()方法的catch部分中检查它。如果!closing,则存在问题,否则这是退出run()的预期异常。
  2. 不要忘记,通常建议在捕获InterruptedException后恢复中断标志。http://www.javapractices.com/topic/TopicAction.do?Id=251
- AnatolyG
@AnatolyG 有时候会调用interrupt()来唤醒正在睡眠或等待可中断锁的线程,而不是为了停止它。在这种情况下,检查interrupted()/isInterrupted()可能不足够。中断的另一个问题是它们会在某个函数执行到一半时中断它,而更好的选择可能是先读取文件直到结束,然后再检查自定义标志。 - Andrea
@Andrea 当然,我可以想象这样的独特设计解决方案 :),但从我的角度来看,这似乎不是像Object wait()/notify()、Locks+Conditions和BlockingQueues等经典的线程间通信机制。我希望能在std lib或任何著名的lib中看到一个例子。至于“在函数执行过程中中断某些功能”,这是一种协作机制(https://www.ibm.com/developerworks/library/j-jtp05236/index.html),这意味着如果这是你的函数,你可以在恢复中断标志并退出之前做必要的操作,例如关闭文件描述符。 - AnatolyG
但是,如果GUI线程只是要求我们的解压函数停止,我更希望函数尽快停止并且不读取和解压我剩下的10GB文件。如果我们想在ExecutorService中使用该函数,服务不知道我们自己的自定义标志,只知道被中断。 - AnatolyG
1个回答

0

要停止一个线程-你可以通过直接调用Thread::interrupt中的interrupt来中断它,或者在您的情况下,调用shutDownNow,这将隐式地调用。或者你可以有一个volatile boolean stop标志,一些其他线程需要设置,你的线程需要检查。

如果你的线程不响应中断,那么shutDownNow除了阻止未来任务被安排外,什么都不会做。


那么,我应该在我的Runnable中手动检查和处理Thread.interrupted()吗? - Serhii Lomov
@ЛомовСергей 你应该捕获那个中断异常。这里有一篇将近20年的文章 - Eugene

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接