【问题标题】:Why does the thread started by ScheduledExecutorService.schedule() never terminate?为什么 ScheduledExecutorService.schedule() 启动的线程永远不会终止?
【发布时间】:2010-05-25 09:15:02
【问题描述】:

当我通过调用 ScheduledExecutorService.schedule() 创建线程时,它在执行计划任务后永远不会终止。

例如以下程序永远不会退出:

public static void main(String[] args) {
  ScheduledFuture scheduledFuture = 
      Executors.newSingleThreadScheduledExecutor().schedule(new Callable() {

    public Void call() {
      doSomething();
      return null;
    }

  }, 1, TimeUnit.SECONDS);
}

public static void doSomething() {
}

这是一个 JDK 错误,还是我错过了什么?

【问题讨论】:

  • doSomething() 中发生了什么?

标签: java concurrency


【解决方案1】:

计划任务正在执行或等待执行。

如果任务正在等待执行,future.cancel() 将阻止它被执行(cancel(true)/cancel(false))。

如果任务已经在执行,future.cancel(false) 将无效。 future.cancel(true) 将中断正在执行该任务的线程。 这是否会产生任何影响取决于您,由谁来执行该任务。取决于实现,任务可能会或可能不会响应中断。

为了使您的任务响应取消,您必须实现doSomething() 以便它响应中断。

基本上有两种方法可以做到这一点:

1.检查逻辑中的中断标志

public void doSomething(){
    stuff();
    //Don't use Thread.interrupt()
    if(Thread.currentThread().isInterrupted()){
        // We have an interruption request, possibly a cancel request
        //Stop doing what you are doing and terminate.
        return;
    }
    doLongRunningStuff();
}  

你必须不时检查中断标志,如果被中断,停止你正在做的事情并返回。请务必使用 Thread.isInterrupted() 而不是 Thread.interrupt() 进行检查。

2.对中断异常采取行动

public void doSomething(){
    try{
        stuff();
    }catch(InterruptedException e){
        // We have an interruption request, possibly a cancel request

        // First, preserve Interrupted Status because InterruptedException clears the 
        // interrupted flag
        Thread.currentThread.interrupt();

        // Now stop doing your task and terminate
        return;
    }

    doLongRunningStuff();
}

当你有任何方法抛出 InterruptedException 时,一定要停止你正在做的事情并在抛出一个时终止。

以这种方式实现方法后,您可以调用 future.cancel(true) 来取消正在运行的任务的执行。

【讨论】:

  • 命令调用后如何关闭调度?从而释放线程资源。毕竟schedule创建的任务执行一次。
  • @Nickolas:不确定您的意思,但如果您的意思是计划运行一次的任务(在延迟等之后)并且它已经完成,那么该任务将已经停止(并且释放线程)。否则我的回答仍然适用。
  • 这是一个不错的答案,但针对的是不同的问题。问题不在于如何终止任务——OP 明确声明该任务是 nop——而是如何终止用于运行任务的线程。
【解决方案2】:

你的程序永远不会终止,因为你创建了一个ScheduledExecutorService,它拥有一个线程池,但你永远不会关闭服务。因此,线程池中的用户线程永远不会被终止,因此虚拟机会一直运行下去。

要解决这个问题,你需要在执行器服务上调用shutdown()。这甚至可以在安排好要执行的任务后直接完成:

public static void main(String[] args) {
  ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
  ScheduledFuture scheduledFuture = executorService.schedule(new Callable() {

    public Void call() {
      doSomething();
      return null;
    }

  }, 1, TimeUnit.SECONDS);
  executorService.shutdown();
}

这将正常执行计划任务,然后终止池中的线程。

【讨论】:

    【解决方案3】:

    您需要调用scheduledExecutorService.shutdown() 来停止执行。否则每秒重启一次。

    (已编辑:见 cmets)

    【讨论】:

    • 应该调用scheduledExecutorService.shutdown()而不是scheduleFuture.shutdown(),而且ScheduledExecutorService.schedule()调用是一次性的,永远不会再重启。
    猜你喜欢
    • 1970-01-01
    • 2015-01-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-13
    • 2013-09-09
    • 1970-01-01
    • 2019-04-28
    相关资源
    最近更新 更多