【问题标题】:Stop a sheduleAtFixedRate task in the ScheduledThreadPoolExecutor Java在 ScheduledThreadPoolExecutor Java 中停止一个 sheduleAtFixedRate 任务
【发布时间】:2020-01-12 23:33:27
【问题描述】:

假设我有这个臃肿的例子。 我期望发生的是在控制台中获得几个数字大约 3 次,然后我期望打印“任务已停止”行,然后我就不会再得到数字了。

所以我想停止执行任务。到目前为止,我已经尝试cancel(true) 未来,executor.shutdownNow)(executor.shutdown() 执行者,但这些都不会导致我上面描述的行为。我错过了什么吗?有没有办法取消这个任务?

        ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
        executor.setRemoveOnCancelPolicy(true);
        final int rareNumber = 302330233;
        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            int x = 0;
            while (true) {
                x++;
                if(x % rareNumber == 0) {
                    System.out.println(x);
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
        Thread.sleep(3_000);
        // I'd like to stop a task here.
        System.out.println("The task is stopped");

【问题讨论】:

    标签: java concurrency scheduled-tasks


    【解决方案1】:

    正确的方法是executor.shutdownNow()。这会导致线程被中断。

    但是你的线程当时没有等待/休眠,所以你没有得到IterruptedException所以你需要检查Thread.intrrupted()标志:

    ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
        executor.setRemoveOnCancelPolicy(true);
        final int rareNumber = 302330233;
        ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
            int x = 0;
            while (true) {
                x++;
                if(x % rareNumber == 0) {
                    System.out.println(x);
                }
    
                if (Thread.interrupted()) {
                    return;
                }
            }
        }, 1, 2, TimeUnit.SECONDS);
        Thread.sleep(3_000);
    
        executor.shutdownNow();
        // I'd like to stop a task here.
        System.out.println("The task is stopped");
    

    【讨论】:

    • 谢谢,这行得通。但是为什么executor看到线程被中断了,为什么不停止任务呢?我已经尝试阅读所涉及类的 Javadocs,但我似乎无法理解。另外,对执行者shutdownNow是不是太苛刻了?如果我以后想重用怎么办?当然,我可以将executor 重新分配给new 执行程序,但似乎有更好的解决方案。
    • 关于中断/停止线程 - docs.oracle.com/javase/8/docs/technotes/guides/concurrency/…。如果你想停止单个任务,你可以调用future.cancel(true) 与上面的任务修改..
    【解决方案2】:

    首先,当您在一次执行中找到正确的数字时,您应该打破while 循环以完成该执行周期。

    ScheduledFuture<?> future = executor.scheduleAtFixedRate(() -> {
        int x = 0;
        while (true) {
            x++;
            if(x % rareNumber == 0) {
                System.out.println(x);
                break;
            }
        }
    }, 1, 2, TimeUnit.SECONDS);
    

    那么你可以按如下方式取消future来停止固定速率调度器。

        Thread.sleep(3_000);
        // I'd like to stop a task here.
        future.cancel(true);
        System.out.println("The task is stopped");
    

    【讨论】:

      【解决方案3】:

      您应该在代码中考虑以下几点:

      • 您永远不会关闭您的池,并且该池中的线程不是守护线程,因此如果您创建您的 Pool ,主 JVM 进程将被阻止关闭 - 因此您应该关闭您的池。关闭池将中断正在处理任务的线程。 ExecutorService docs 中描述了如何做到这一点:
      void shutdownAndAwaitTermination(ExecutorService pool) {
         pool.shutdown(); // Disable new tasks from being submitted
         try {
           // Wait a while for existing tasks to terminate
           if (!pool.awaitTermination(60, TimeUnit.SECONDS)) {
             pool.shutdownNow(); // Cancel currently executing tasks
             // Wait a while for tasks to respond to being cancelled
             if (!pool.awaitTermination(60, TimeUnit.SECONDS))
                 System.err.println("Pool did not terminate");
           }
         } catch (InterruptedException ie) {
           // (Re-)Cancel if current thread also interrupted
           pool.shutdownNow();
           // Preserve interrupt status
           Thread.currentThread().interrupt();
         }
       }
      
      • 如果你想让你的任务可以取消,有两种常见的方法:

        1. 在循环中检查Thread.currentThread().isInterrupted() 状态
        2. 如果你调用了一些阻塞的方法并且可以抛出InterruptedException你可以处理这个异常。线程中断时会抛出。
      • 正如您设置的executor.setRemoveOnCancelPolicy(true);,如果线程被中断,则不会重新创建线程。如果您没有设置此标志 - 保留中断状态是个好主意,因为您不是这些线程的所有者,并且您不知道中断对它们意味着什么。但是你没有使用任何可以抛出InterruptedException的方法,所以没关系。

      【讨论】:

      • 感谢您的回答!如果传递给服务的Runnable 不是这个循环,而是其他一些可能阻塞的计算怎么办?假设我需要在我的Runnable 中发推文。我想我会发送一个 HTTP 请求,或者访问某种 Java Twitter API,这可能很耗时。如果这样的Runnable 在某些计算过程中被future.cancel() 取消了怎么办?这是否意味着我必须在进行计算之前检查Thread.interrupted()
      • Future.cancel 将为正在处理任务的线程设置中断标志。如果您希望它可以取消,您的任务应该以我在回答中描述的两种方式之一响应取消。
      猜你喜欢
      • 2013-01-31
      • 2013-05-20
      • 1970-01-01
      • 2016-09-16
      • 1970-01-01
      • 1970-01-01
      • 2020-10-10
      • 2019-11-15
      • 1970-01-01
      相关资源
      最近更新 更多