Java 如何中断给定未来对象的线程?

Java 如何中断给定未来对象的线程?,java,multithreading,threadpool,executorservice,futuretask,Java,Multithreading,Threadpool,Executorservice,Futuretask,我想启动一个线程,如果它没有在5秒内完成,则取消它: private final class HelloWorker implements Callable<String> { public String call() throws Exception { while(true) { if (Thread.isInterrupted()) { return null; }

我想启动一个线程,如果它没有在5秒内完成,则取消它:

private final class HelloWorker implements Callable<String> {
    public String call() throws Exception {
        while(true) {
            if (Thread.isInterrupted()) {
                return null;
            }
        }
        return performExpensiveComputation();
    }

    private String performExpensiveComputation() {
        // some blocking expensive computation that may or may not take a very long time
    }
}

private ExecutorService executorService = Executors.newFixedThreadPool(threadPoolSize);
Future<String> future = executorService.submit(new HelloWorker());

try {  
    String s = future.get(5, TimeUnit.SECONDS);
} catch (TimeoutException e) {
    future.cancel(true);

    System.out.println("cancelled: " + future.isCancelled() + "done: " + future.isDone());

    executorService.shutdown();

    try {
        System.out.println("try to terminate: " + executorService.awaitTermination(60, TimeUnit.SECONDS));
    } catch (Exception ex) {
        // ignore
    }
}
private final类HelloWorker实现了可调用{
公共字符串调用()引发异常{
while(true){
if(Thread.isInterrupted()){
返回null;
}
}
返回performExpensiveComputation();
}
专用字符串performExpensiveComputation(){
//一些阻塞可能需要或不需要很长时间的昂贵计算
}
}
私有ExecutorService ExecutorService=Executors.newFixedThreadPool(threadPoolSize);
Future=executorService.submit(新HelloWorker());
试试{
字符串s=future.get(5,TimeUnit.SECONDS);
}捕获(超时异常e){
future.cancel(true);
System.out.println(“取消:”+future.isCancelled()+“完成:”+future.isDone());
executorService.shutdown();
试一试{
System.out.println(“尝试终止:”+executorService.waittermination(60,TimeUnit.SECONDS));
}捕获(例外情况除外){
//忽略
}
}

然而,它看起来像是终止返回false。有没有办法让我检查为什么ExecutorService不会终止?我可以找出哪些线程仍在运行吗?

没有安全的方法可以停止正在运行的线程而不影响进程其余部分的稳定性。这就是为什么
Thread#stop
早就被弃用的原因,也是为什么Executor服务只使用软的、协作的
Thread#interrupt
机制的原因


您的线程必须主动检查是否请求了中断,并在结束之前执行适当的清理。或者,线程将调用一些可中断的JDK方法,这些方法将抛出
InterruptedException
,tread将正确地执行并结束它自己。

没有安全的方法可以停止正在运行的线程而不干扰进程其余部分的稳定性。这就是为什么
Thread#stop
早就被弃用的原因,也是为什么Executor服务只使用软的、协作的
Thread#interrupt
机制的原因

您的线程必须主动检查是否请求了中断,并在结束之前执行适当的清理。或者,线程将调用一些可中断的JDK方法,这些方法将抛出
InterruptedException
,tread将正确地执行并结束它自己。

为什么Future.cancel()不能按您认为的方式工作。

Future cancel从正在运行的队列中删除任务。如果您的任务已经在运行,它将不会停止它。因此,cancel()是一个与中断不同的概念。正如Javadocs所说:

试图取消此任务的执行。如果 该任务已完成、已取消或可能无法执行 不能因为其他原因取消。如果成功,此任务 调用cancel时未启动,此任务不应运行。如果 任务已启动,然后使用MayInterruptFrunning参数 确定执行此任务的线程是否应 在试图停止任务时被中断。

你问的是如何打断别人。幸运的是,当您调用Future.cancel()时,它将调用中断方法。但您需要允许它带有MayInterruptFrunning标志,并且需要正确处理中断(见下文)

为什么要打断?

当您有一个长时间运行的任务需要停止时,或者当您有一个守护进程需要关闭时,以及其他示例中,在Java中中断线程非常有用

如何中断

要中断,请在线程上调用interrupt()。这是一个协作过程,因此您的代码必须为此做好准备。像这样:

myThread.interrupt();
责任代码

代码的责任是为任何中断做好准备。我想说的是,每当您有一个长时间运行的任务时,您都会插入一些中断准备代码,如下所示:

while (... something long...) {

     ... do something long

     if (Thread.interrupted()) {
         ... stop doing what I'm doing...
     }
}
如何停止我正在做的事情?

您有几个选择:

  • 如果您在Runnable.run()中,只需返回或中断循环并完成该方法
  • 您可能在代码的深处使用了其他方法。在这一点上,该方法抛出InterruptedException可能是有意义的,因此您只需这样做(清除标志)
  • 但在代码的深处,抛出中断异常可能没有意义。在这种情况下,您应该抛出一些其他异常,但在此之前,请标记线程再次中断,以便捕获的代码知道中断正在进行。下面是一个例子:
  • 现在,异常可以传播一个中断,或者终止线程,或者被捕获,但是接收代码有望注意到中断正在进行

    我应该使用isInterrupted()还是interrupted()

    您应该更喜欢中断()
    ,因为:

  • 您的代码应该重置中断标志,因为如果您不这样做,您正在使用的线程可能返回到一个线程池,该线程池的中断状态会导致问题(当然,这是线程池代码中的一个错误,如果您使用Executors.newFixedThreadPool()例如,但其他线程代码也可能有
  • 正如另一个答案所述,清除interrupted标志表示您已收到消息并正在采取行动。如果您将其设置为true,则稍后的来电者可能会认为您不会及时响应该消息
  • 为什么中断()为什么我的代码中没有其他标志?

    中断是最好的中断机制,因为我们的代码已经准备好了。如果我们发现代码只是捕获并忽略了中断异常s,或者没有检查f
    private void someMethodDeepDown() {
        while (.. long running task .. ) {
              ... do lots of work ...
    
              if (Thread.interrupted()) {
                 // oh no! an interrupt!
                 Thread.currentThread().interrupt();
                 throw new SomeOtherException();
              }
         }
    }