Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/347.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/google-sheets/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 执行器关闭后,ScheduledFuture.get()仍被阻止_Java_Multithreading_Concurrency - Fatal编程技术网

Java 执行器关闭后,ScheduledFuture.get()仍被阻止

Java 执行器关闭后,ScheduledFuture.get()仍被阻止,java,multithreading,concurrency,Java,Multithreading,Concurrency,我想在后台运行一些定期任务,我想把它做好。 因此,我使用ScheduledExecutorService.scheduleWithFixedDelay(..)调度任务,并在单独的线程上调用ScheduledFuture.get(),以控制任务的生命周期,捕获任务中未捕获的异常,并在任务取消时收到通知 问题是,如果在任务执行时调用了ScheduledExecutorService.shutdown(),则不会收到通知,其ScheduledFuture方法将永远被阻止 下面是说明问题的简单代码: p

我想在后台运行一些定期任务,我想把它做好。
因此,我使用
ScheduledExecutorService.scheduleWithFixedDelay(..)
调度任务,并在单独的线程上调用
ScheduledFuture.get()
,以控制任务的生命周期,捕获任务中未捕获的异常,并在任务取消时收到通知

问题是,如果在任务执行时调用了
ScheduledExecutorService.shutdown()
,则不会收到通知,其
ScheduledFuture
方法将永远被阻止

下面是说明问题的简单代码:

public final class SomeService {
    private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
    private final SomePeriodicTask task = new SomePeriodicTask();
    private ScheduledFuture<?> future;

    public void start() {
        future = executor.scheduleWithFixedDelay(task, 0, 1, TimeUnit.SECONDS);

        final Runnable watchdog = new Runnable() {
            @Override
            public void run() {
                try {
                    future.get();
                } catch (CancellationException ex) {
                    System.out.println("I am cancelled");
                } catch (InterruptedException e) {
                    System.out.println("I am interrupted");
                } catch (ExecutionException e) {
                    System.out.println("I have an exception");
                }
                System.out.println("Watchdog thread is exiting");
            }
        };
        new Thread(watchdog).start();
    }

    public void shutdownAndWait() {
        System.out.println("Shutdown requested");
        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS);
        } catch (InterruptedException e) { //BTW When and why could this happen?
            System.out.println("Waiting for shutdown was interrupted");
        }
        System.out.println("Executor is shutdown " + executor.isShutdown());
    }
}
如果我们像这样运行它

public static void main(String[] args) throws InterruptedException {
    SomeService service = new SomeService();
    service.start();
    Thread.sleep(3000);
    service.shutdownAndWait();
    System.out.println("Future is cancelled " + service.future.isCancelled());
    System.out.println("Future is done " + service.future.isDone());
}
然后输出为

I am just doing my job...done
I am just doing my job...done
I am just doing my job...done
Shutdown requested
I am cancelled
Watchdog thread is exiting
Executor is shutdown true
Future is cancelled true
Future is done true
完全如所料

但是如果我们修改任务来模拟一些繁重的工作

final class SomePeriodicTask implements Runnable{
    @Override
    public void run() {
        System.out.print("I am just doing my job...");
        try {
            Thread.sleep(1500); //Heavy job. You can change it to 5000 to be sure
        } catch (InterruptedException e) {
            System.out.println("Task was interrupted");
        }
        System.out.println("done");
    }
}
因此对
shutdown()
的调用在任务执行时发生。。。然后输出为

I am just doing my job...done
I am just doing my job...Shutdown requested
done
Executor is shutdown true
Future is cancelled false
Future is done false
所以这里发生了什么。。。执行器正在按预期关闭。它允许当前任务按预期完成其工作。我们看到executor确实完成了关闭,但是我们的
ScheduledFuture
没有被取消,它的
get()
方法仍然被阻止,监视线程阻止JVM退出并永远挂起

当然也有变通办法。例如,我可以在关机之前手动调用
future.cancel(false)
,或者将
watchdog
设置为守护进程线程,甚至尝试自己安排Executor的关机,以便它不会与正在运行的任务重叠。。。但以上所有这些都有缺点,当代码变得更复杂时,事情可能会偏离正轨

不管怎么说,我正在寻求你的专家意见,因为在我理解它为什么不表现得像应有的那样之前,我不会有安宁。如果它是jdk中的一个bug,则必须报告它。如果我误解了什么,我的代码是错误的,我必须知道它


提前感谢

首先要了解的是,对于周期性任务,正常完成不会将未来的状态变为完成,因为它预期会重新安排并可能重新运行

这会干扰执行器.shutdown的语义;它将取消所有挂起的任务,但让当前正在运行的任务完成。因此,当前正在运行的任务正常完成,不会像以前那样将其状态设置为
done
,但不会重新安排,因为执行器已关闭

即使您使用
shutdownow
它也会中断当前正在运行的任务,但不会取消它们,而且由于您的任务捕获了
InterruptedException
,并在正常情况下提前完成,因此不会有状态转换到
done

添加所需行为(即使在关机时正常完成的任务也会被取消)的最佳位置是
Executor
实现;换线就行了

private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();

private final ScheduledExecutorService executor=new ScheduledThreadPoolExecutor(1){
@凌驾
执行后受保护的无效(可运行的r、可丢弃的t){
if(t==null&&IsShutton()&&r RunnableScheduledFuture的实例)
{
RunnableScheduledFuture rsf=(RunnableScheduledFuture)r;
如果(rsf.isPeriodic())rsf.cancel(false);
}
};
};

@Holder非常感谢您的帮助,但这并没有回答我的问题。问题是,为什么ScheduledFuture.get()会被无限期地阻止,即使执行器很早就关闭并消失了。它应该返回null或抛出某种异常或任何您命名的异常,但不能永远挂在那里。这种行为没有得到适当的记录,在我看来确实是错误的。我倾向于认为它是一个错误。我打印未来的状态只是为了详细说明,以表明未来不会收到任何关机通知。我不在乎它的实际……状态。也许这让人困惑,我应该把它删掉。您的解决方案也是一种变通方法。我在问题中提到了其中三个,你提出了第四个。肯定还有更多,但我仍然想知道为什么
ScheduledFuture.get()
在关机时会被严重阻止,以及为什么它被认为是正确的行为。我确实解释了这种行为。你是否关心未来的状况并不重要,但未来关心你。它的
get
方法永远不会返回,只要未来没有完成。而且周期性的未来不会完成。如果你不想解决问题,就不要创建“看门狗”。对于定期任务的未来,不要调用
get
,因为很明显,它永远无法正常完成。
private final ScheduledExecutorService executor = Executors.newSingleThreadScheduledExecutor();
private final ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(1) {
  @Override
  protected void afterExecute(Runnable r, Throwable t) {
    if(t==null && isShutdown() && r instanceof RunnableScheduledFuture<?>)
    {
      RunnableScheduledFuture<?> rsf = (RunnableScheduledFuture<?>)r;
      if(rsf.isPeriodic()) rsf.cancel(false);
    }
  };
};