一旦我认为它已完成,如何在 ScheduledThreadPoolExecutor 中停止任务

Posted

技术标签:

【中文标题】一旦我认为它已完成,如何在 ScheduledThreadPoolExecutor 中停止任务【英文标题】:How to stop a task in ScheduledThreadPoolExecutor once I think it's completed 【发布时间】:2013-01-31 02:23:57 【问题描述】:

我有一个 ScheduledThreadPoolExecutor,我用它安排一个任务以固定速率运行。我希望任务以指定的延迟运行最多 10 次,直到它“成功”。在那之后,我不希望任务被重试。所以基本上我需要在我想要停止时停止运行计划任务,但不关闭 ScheduledThreadPoolExecutor。知道我会怎么做吗?

这是一些伪代码 -

public class ScheduledThreadPoolExecutorTest

  public static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15);  // no multiple instances, just one to serve all requests

  class MyTask implements Runnable
  
    private int MAX_ATTEMPTS = 10;
    public void run()
    
      if(++attempt <= MAX_ATTEMPTS)
      
        doX();
        if(doXSucceeded)
        
          //stop retrying the task anymore
        
      
      else
       
        //couldn't succeed in MAX attempts, don't bother retrying anymore!
      
    
  

  public void main(String[] args)
  
    executor.scheduleAtFixedRate(new ScheduledThreadPoolExecutorTest().new MyTask(), 0, 5, TimeUnit.SECONDS);
  

【问题讨论】:

【参考方案1】:

运行这个测试,它会打印1 2 3 4 5 并停止

public class ScheduledThreadPoolExecutorTest 
    static ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(15); // no
    static ScheduledFuture<?> t;

    static class MyTask implements Runnable 
        private int attempt = 1;

        public void run() 
            System.out.print(attempt + " ");
            if (++attempt > 5) 
                t.cancel(false);
            
        
    

    public static void main(String[] args) 
        t = executor.scheduleAtFixedRate(new MyTask(), 0, 1, TimeUnit.SECONDS);
    

【讨论】:

非常感谢!我确实知道 schedulefuture,但我试图使用这种方式: t = executor.scheduleAtFixedRate(new MyTask(), 0, 1, TimeUnit.SECONDS); t.取消(真);由于明显的原因,这永远不会奏效。在任务实现中取消任务似乎是正确的。 谢谢。这有帮助。有趣的是,这个例子永远不会退出。我认为您需要关闭执行程序 或者更确切地说,您需要一个 t.get() 和一个在 CancellationException 的 catch 块内关闭的执行程序 这种方法线程安全吗?任务执行可能会在主线程中分配之前到达 t.cancel() 导致 NullPointerException。【参考方案2】:

在线程外很好地取消:

public class ScheduleTest 

    @Test
    public void testCancel() throws Exception 
        final ScheduledThreadPoolExecutor EXECUTOR = (ScheduledThreadPoolExecutor) Executors.newScheduledThreadPool(2);
        ScheduledFuture f1 = EXECUTOR.scheduleAtFixedRate(new Runnable() 
            @Override
            public void run() 
                System.out.println("Im alive 1");
            
        , 0, 1, TimeUnit.SECONDS);
        ScheduledFuture f2 = EXECUTOR.scheduleAtFixedRate(new Runnable() 
            @Override
            public void run() 
                System.out.println("Im alive 2");
            
        , 0, 2, TimeUnit.SECONDS);

        Thread.sleep(10000);
        f1.cancel(true);
        System.out.println("f1 cancel");
        Thread.sleep(10000);
        f2.cancel(false);
        System.out.println("f2 cancel");
        Thread.sleep(10000);
    

有时线程无法取消,这通常通过volatile boolean isCancelled;解决

【讨论】:

很好的例子,因为这个问题,一段时间以来面临很多问题......谢谢:)【参考方案3】:

CountDownLatch 是另一种方法。当线程完成时,调用锁上的countDown()。调用线程调用latch.await(),直到所有线程完成。此时调用ExecutorService.shutdownNow(),这样你的主线程就不会变成僵尸。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledThreadPoolExecutorTest 

  static int i = 0;

  public static void main(String[] args) throws Exception 
    final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
    final CountDownLatch latch = new CountDownLatch(1);
    executor.scheduleAtFixedRate(() -> 
        System.out.println(++i);
        if (i > 4) 
          latch.countDown();
        
    , 0, 100, TimeUnit.MILLISECONDS);
    latch.await();
    executor.shutdownNow();
  

【讨论】:

以上是关于一旦我认为它已完成,如何在 ScheduledThreadPoolExecutor 中停止任务的主要内容,如果未能解决你的问题,请参考以下文章

如何将 HiveQL 查询的结果输出到 CSV?

加载完成后,Gmail 如何强制自己成为活动标签?

一旦第一组工作线程完成处理,我如何使用 AutoResetEventHandler 向主线程函数发出信号以再次启动线程

中国手机品牌争论谁是国内第一,而它已成为中国手机在海外的代表

如何运行多个firebase承诺,然后一旦完成,执行功能

如何使用 ObservableObject 更新 UIViewRepresentable