Как остановить задачу в ScheduledThreadPoolExecutor, когда я думаю, что она завершена

У меня есть 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);
    }
}

Ответ 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();
  }
}