Java 立即停止线程的最佳实践

Java 立即停止线程的最佳实践,java,multithreading,Java,Multithreading,我有上下文,可以用来预测。基于上下文进行预测的算法不是我问题的一部分。但这是昂贵的时间,所以我把它从主线程中移出。在任何时候,上下文都可能发生变化。当这种情况发生时,应用程序应该用新的输入重新启动。与旧上下文相关的预测将失效,因此其计算应中断 现在,我使用代码: public class BackgroundTasksManager { public static final BackgroundTasksManager shared = new BackgroundTasksManager(

我有上下文,可以用来预测。基于上下文进行预测的算法不是我问题的一部分。但这是昂贵的时间,所以我把它从主线程中移出。在任何时候,上下文都可能发生变化。当这种情况发生时,应用程序应该用新的输入重新启动。与旧上下文相关的预测将失效,因此其计算应中断

现在,我使用代码:

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();
    });
}
公共类后台任务管理器{
public static final BackgroundTasksManager shared=new BackgroundTasksManager();
私有ExecutorService ConceptResultExecutor=null;
私有BackgroundTasksManager(){}
public void startConceptResultsCalculation(概念结果计算输入,
消费者回调){
ThreadGroup callbackGroup=Thread.currentThread().getThreadGroup();
if(conceptResultExecutor!=null)
conceptResultExecutor.shutdownNow();
ConceptResultExecutor=Executors.newSingleThreadExecutor();
ConceptResultExecutor.submit(()->{
ConceptResultsCalculationOutput输出=ConceptResultsCombinator.PossibileResults(输入);
线程callbackThread=新线程(callbackGroup,()->callback.accept(output));
callbackThread.start();
});
}

因此,当我需要开始预测计算时,我关闭旧的执行器并创建一个新的执行器。这个解决方案是有效的,还是隐藏了一些危险的细微差别?

要停止线程,您可以通过直接调用
线程::中断
来中断它,或者在您的情况下,调用
立即关闭
,这将是隐式的或者你可以有一个
volatile boolean stop
标志,其他线程需要设置,你的线程需要检查


如果您的线程不响应中断,
Shutdownow
除了阻止在其上计划的未来任务外,将不会做任何事情。

您可以使用Thread::stop。该方法从很长一段时间以来(从1.2开始)就被弃用,但在Java 15中仍然存在。中所述方法的问题是

此方法本身不安全。使用thread.stop停止线程 使其解锁所有已锁定的监视器(作为 未经检查的ThreadDeath异常传播的自然结果 如果以前受这些保护的任何对象 监视器处于不一致的状态,损坏的对象变为 对其他线程可见,可能导致任意行为。 stop的许多用法应该被简单地修改某些代码的代码所取代 变量,指示目标线程应停止运行 目标线程应定期检查此变量,并从 如果变量指示 是停止运行。如果目标线程等待很长时间(在 例如,条件变量),应使用中断方法 打断等待

这个问题不仅与锁定有关,而且与在数据不一致的中间会突然中断的任何阐述有关。 但您可以使用try finally进行相同的清理

void run() {
    try {
       runInner();
    }
    finally {
        //do same checks
        //this is executed also if stop() is called where execution was inside runInner()
    }
插入一些支票的方法

volatile boolean myThreadStopping=false;

void run() {
    // some code
    if (myThreadStopping) return;
    // some code
    if (myThreadStopping) return;
    for (...) {
       // some code
       if (myThreadStopping) return;
    }
}
具有控制退出点的优势,它允许您在从任务返回时保持数据和锁的一致性。如果代码中存在任何睡眠,则可以使用Thread::interrupt after myThreadStopping=true

活动检查的最大限制是阻塞IO方法。例如,如果在代码中从套接字的InputStream读取,Thread::interrupt将不会帮助您执行myThreadStopping变量的下一次检查,线程将在read()处保持等待


您可以将这两种方法结合使用。

因此,我应该手动检查并处理Thread.interrupted()在我的Runnable?中,您应该捕获中断的异常。java平台中有一个标准机制来中断线程。这是中断标志。线程应该自己检查该标志。任何长/阻塞操作都应该检查该标志。例如,如果您有一个长时间运行的循环,您可以应该定期检查interrupted()/isInterrupted()标志。顺便说一句,要中断socket.read()之类的阻塞IO操作,最好的方法是关闭另一个线程中的套接字()以退出read()立即执行IOException。2.不需要使用合成volatile标志来停止线程计算,但这可能是用户将停止线程的标志,因此,请将标志closing设置为true,并在run()的catch部分检查它方法。如果!关闭,则说明您有问题,否则这将从run()退出异常。3.不要忘记,通常建议在捕获InterruptedException@AnatolyG ameous interrupt()后恢复interrupted标志调用以唤醒正在睡眠或等待可中断锁的线程,而不打算停止它。在这种情况下,检查interrupted()/isInterrupted()不够的。中断的另一个问题是,它们中断中间的一些函数,相反,它可以是更好的,例如,读取文件到末端,然后检查自定义标志。“安德列,当然,我可以想象这种独占的设计解决方案:”但是,从我的角度来看,这似乎不是规范的线程间的合作。通信机制,如Object wait()/notify()、Locks+Conditions、BlockingQueues等。我想在std库或任何已知库中看到一个示例。至于“中间中断某个函数”,这是协作机制(),这意味着如果这是您的函数,您可以在恢复中断标志并退出之前执行所需的操作,例如,关闭文件描述符。但如果GUI线程只是要求解压函数停止,我希望函数尽快停止,不要读取和解压我的10gigs文件的其余部分