27

Suppose I have a task that is pulling elements from a java.util.concurrent.BlockingQueue and processing them.

public void scheduleTask(int delay, TimeUnit timeUnit)
{
    scheduledExecutorService.scheduleWithFixedDelay(new Task(queue), 0, delay, timeUnit);
}

How can I schedule / reschedule the task if the frequency can be changed dynamically?

  • The idea is to take a stream of data updates and propagate them in batch to a GUI
  • The user should be able to vary the frequency of updates
parkr
  • 3,188
  • 5
  • 35
  • 39
  • it's not clear to me why you are using a blocking queue. if youe queue is empty. I assume your scheduled task will be blocked. is that your intention? this will probably confuse the task scheduler timing. – Omry Yadan Oct 05 '09 at 10:07
  • I chose the ArrayBlockingQueue implementation as it has to be threadsafe, respect FIFO ordering and be bounded. Even if the task blocks, it shouldn't confuse the task scheduling should it? – parkr Oct 05 '09 at 10:15
  • You are correct in using a BlockingQueue implementation (in fact ScheduledThreadPoolExecutor uses one internally). However, why are you propagating your updates to the GUI using a timer at all? Why not do it in real-time? Are there too many updates? Are you concerned about the Swing thread spinning? – Adamski Oct 05 '09 at 10:43
  • I want to batch the updates. So internally the events may arrive every ms, but the gui will refresh every 100ms. I think this will be more appealing visually and have less cpu overhead. I'm using Eclipse RCP instead of Swing. – parkr Oct 05 '09 at 11:28
  • Later duplicate: [*How do I change the rate or period of a repeating task using ScheduledExecutorService?*](https://stackoverflow.com/q/28620806/642706) – Basil Bourque Oct 14 '18 at 23:10

5 Answers5

37

Use schedule(Callable<V>, long, TimeUnit) rather than scheduleAtFixedRate or scheduleWithFixedDelay. Then ensure that your Callable reschedules itself or a new Callable instance at some point in the future. For example:

// Create Callable instance to schedule.
Callable<Void> c = new Callable<Void>() {
  public Void call() {
   try { 
     // Do work.
   } finally {
     // Reschedule in new Callable, typically with a delay based on the result
     // of this Callable.  In this example the Callable is stateless so we
     // simply reschedule passing a reference to this.
     service.schedule(this, 5000L, TimeUnit.MILLISECONDS);
   }  
   return null;
  }
}

service.schedule(c);

This approach avoids the need to shut down and recreate the ScheduledExecutorService.

Steve McLeod
  • 51,737
  • 47
  • 128
  • 184
Adamski
  • 54,009
  • 15
  • 113
  • 152
  • 1
    Instead of `Callable` you can (should?) use `Runnable`. – Thirler Nov 21 '14 at 09:56
  • I don't find the `schedule(Callable)` function for a ScheduledExecutorService. Only the ones with all parameters. Could you please point me to where? Or at least fixed the example to include a delay of 0. – jlanza Jan 31 '17 at 20:17
  • 1
    http://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html#schedule-java.util.concurrent.Callable-long-java.util.concurrent.TimeUnit- – Adamski Feb 06 '17 at 12:19
8

I don't think you can change a fixed rate delay. I think you need to use schedule() to perform a one-shot, and schedule again once that has completed (with a modified time out if required).

ThomasH
  • 830
  • 1
  • 8
  • 23
Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • 1
    Thanks - I made 'delay' an instance variable and added a private method to do the following: while(!executorService.isShutdown) { executorService.schedule(new Task(queue), delay, TimeUnit.MILLISECONDS); } – parkr Oct 06 '09 at 01:52
6

I had to do this recently using ScheduledFuture and didn't want to wrap Runnable or such. Here's how I did it:

private ScheduledExecutorService scheduleExecutor;
private ScheduledFuture<?> scheduleManager;
private Runnable timeTask;

public void changeScheduleTime(int timeSeconds){
    //change to hourly update
    if (scheduleManager!= null)
    {
        scheduleManager.cancel(true);
    }
    scheduleManager = scheduleExecutor.scheduleAtFixedRate(timeTask, timeSeconds, timeSeconds, TimeUnit.SECONDS);
}

public void someInitMethod() {

    scheduleExecutor = Executors.newScheduledThreadPool(1);    
    timeTask = new Runnable() {
        public void run() {
            //task code here
            //then check if we need to update task time
            if(checkBoxHour.isChecked()){
                changeScheduleTime(3600);
            }
        }
    };

    //instantiate with default time
    scheduleManager = scheduleExecutor.scheduleAtFixedRate(timeTask, 60, 60, TimeUnit.SECONDS);
}
Chibueze Opata
  • 9,856
  • 7
  • 42
  • 65
2

Shouldn't you be using scheduleAtFixedRate if you are trying to process several queue tasks with a specific interval? scheduleWithFixedDelay will only wait for the specified delay and then execute one task from the queue.

In either case, the schedule* methods in a ScheduledExecutorService will return a ScheduledFuture reference. If you want to change the rate, you can cancel the ScheduledFuture and reschedule the task with a different rate.

jarnbjo
  • 33,923
  • 7
  • 70
  • 94
  • scheduleWithFixedDelay(...) - Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given delay between the termination of one execution and the commencement of the next. If any execution of the task encounters an exception, subsequent executions are suppressed. Otherwise, the task will only terminate via cancellation or termination of the executor. – parkr Oct 05 '09 at 10:17
  • 1
    Can you give a code example of cancelling and rescheduling? What about any updates in progress? – parkr Oct 05 '09 at 10:19
0

scheduleWithFixedDelay(...) returns a RunnableScheduledFuture. In order to reschedule it, you might just cancel and reschedule it. To reschedule it, you may just wrap the RunnableScheduledFuture wit a new Runnable:

new Runnable() {
    public void run() {
        ((RunnableScheduledFuture)future).run();
    }
};
sfussenegger
  • 35,575
  • 15
  • 95
  • 119