How to stop a task in ScheduledThreadPoolExecutor once I think it's completed

41,978

Solution 1

run this test, it prints 1 2 3 4 5 and stops

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);
    }
}

Solution 2

Nicely cancelled outside of thread:

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);
    }
}

Sometimes thread couldn't be cancelled, this solved usually via volatile boolean isCancelled;

Solution 3

CountDownLatch is an alternative approach. When the thread completes, call countDown() on the latch. The calling thread calls latch.await() until all threads complete. At that point call ExecutorService.shutdownNow() so that your main thread doesn't turn into a zombie.

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();
  }
}
Share:
41,978
mystarrocks
Author by

mystarrocks

A software programmer and a tech enthusiast. On Disqus as mystarrocks. #SOreadytohelp

Updated on September 30, 2020

Comments

  • mystarrocks
    mystarrocks over 3 years

    I have a ScheduledThreadPoolExecutor with which I schedule a task to run at a fixed rate. I want the task to be running with a specified delay for a maximum of say 10 times until it "succeeds". After that, I will not want the task to be retried. So basically I'll need to stop running the scheduled task when I want it to be stopped, but without shutting down the ScheduledThreadPoolExecutor. Any idea how I'd do that?

    Here's some pseudocode -

    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);
      }
    }
    
  • mystarrocks
    mystarrocks over 11 years
    Perfect thanks! I did know about the scheduledfuture but I was trying to use this way: t = executor.scheduleAtFixedRate(new MyTask(), 0, 1, TimeUnit.SECONDS); t.cancel(true); which would never have worked for obvious reasons. Canceling the task inside the task implementation seems about right.
  • Luffy
    Luffy over 9 years
    Nice Example looking this from a while facing lots of issues because of this.... Thanks :)
  • Kirby
    Kirby over 7 years
    Thanks. This helped. Interesting to note that this example never exits. I think you need an executor shutdown
  • Kirby
    Kirby over 7 years
    Or rather you need a t.get() and an executor shutdown inside a catch block of CancellationException
  • Sergey Podolsky
    Sergey Podolsky over 6 years
    Is this approach thread safe? Task execution may reach t.cancel() before it is assigned in the main thread causing NullPointerException.