migrating from Handler to ScheduledExecutorService for scheduling - java

My goal is to schedule a recurrent job that happens on a non-even rate. I am going to migrate from first snippet to the second:
1st:
Handler mHandler = new Handler() {
#Override
public void handleMessage(Message msg) {
if (msg.what == MSG1) {
//recurrent job here
long nextTime = nextTime();
sendMessageAtTime(obtainMessage(MSG1), nextTime);
}
}
}
};
2nd:
ScheduledExecutorService mExecutor;
while (true){
mExecutor.schedule(new Callable() {
public Object call() throws Exception {
long startTime = SystemClock.uptimeMillis();
//recurrent job here
delay = nextTime() - startTime ;
return true;
}
}, delay, TimeUnit.MILLISECONDS);
}
My questions are:
1- is it true in the first snippet that the thread, to which the mHandler is referring, is free between jobs to do other tasks or handle other messages?
2- However in the second snippet, Thread is always busy doing the loop. right?
3- How can I rewrite the second code so that I won't loose thread activity between jobs (in delays)?
Any help is highly appreciated

Your second code won't work as expected. After the first task has been scheduled and is waiting to be executed, the while loop continues to schedule more tasks, all of them with the same delay. So you'll end up having thousands, probably millions of tasks. And of course, because the main thread is running an infinite loop without any wait, it is busy all the time. This is probably not what you want.
You should better use a simliar approach than the handler uses above:
final ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.schedule(() -> {
// do work
// reschedule
executor.schedule(this, nextTime() - System.currentTimeMillis());
}, delay, TimeUnit.MILLISECONDS);
(Of course you should also check that the delay you specify when rescheduling is not negative).
Update: If you need to process the result of each execution individually, another approach similar to your second code example is possibly what you want. It schedules the task executions insisde a loop and hands over the result to a Consumer, as soon as it is available. (Note the future.get() inside the loop which causes the looping thread to pause until the task is done).
public static <T> void schedule(ScheduledExecutorService scheduler,
Schedule schedule, Callable<T> task, Consumer<? super T> consumer)
throws InterruptedException, ExecutionException {
while (true) {
if (Thread.interrupted()) throw new InterruptedException();
long delay = schedule.nextTime() - System.currentTimeMillis();
if (delay < 0) continue; // skip this step
ScheduledFuture<? extends T> future = scheduler.schedule(task,
delay, schedule.getUnit());
consumer.accept(future.get());
}
}
Also note the interruption check, so that other threads can stop execution by interrupting the looping thread. This simplifies the usage of this method inside another task in case you want to run it on a background thread too.
Schedule could be a functional interface that provides access to the scheduling information:
#FunctionalInterface
public interface Schedule {
long nextTime();
default TimeUnit getUnit() { return TimeUnit.MILLISECONDS; }
}
Btw.: The android.os.Handler is a very nice way to do what you want in android. So you should only migrate to ScheduledExecutorService if you really need its features (e.g. getting a Future result).

public class RecurrentJobThatHappensOnANonEvenRate {
/**
* Consider you have your job defined as below
*/
abstract class TheJob implements Runnable {
#Override
public void run() {
long startTime = System.currentTimeMillis();
doRecurrentJob();
schedule(nextTime() - startTime);
}
void doRecurrentJob() {
// Do the job
}
long nextTime() {
// calculate next execution time
long randomDelay = Math.round(5000 + Math.random() * 5000);
return System.currentTimeMillis() + randomDelay;
}
public abstract void schedule(long delay);
};
/**
* Example using `ScheduledExecutorService`.
*/
public void exampleWithScheduledExecutorService() {
TheJob theJob = new TheJob() {
private final ScheduledExecutorService executor =
Executors.newScheduledThreadPool(1);
#Override
public void schedule(long delay) {
executor.schedule(this, delay, TimeUnit.MILLISECONDS);
}
};
theJob.schedule(1500);
}
/**
* Example with `Handler` and using already existing `Thread` with
* `Looper` (most probably the main looper).
*/
public void exampleWithHandlerAndMainLooper() {
TheJob theJob = new TheJob() {
private final Handler handler =
// new Handler();
// or if you are not in the main thread:
new Handler(Looper.getMainLooper());
#Override
public void schedule(long delay) {
handler.postDelayed(this, delay);
}
};
theJob.schedule(1500);
}
/**
* Example with `Handler` and `HandlerThread` (a convenience thread
* class with looper).
*/
public void exampleWithHandlerAndHandlerThreadsLooper() {
TheJob theJob = new TheJob() {
private final HandlerThread handlerThread;
private final Handler handler;
private final long killThreadAt;
{
handlerThread = new HandlerThread("myThread");
// handler thread must be quit when you no longer use it.
// see nextTime() method below.
killThreadAt = System.currentTimeMillis() + 30000;
// alternatively you can set it to be a daemon thread.
// handlerThread.setDaemon(true);
handlerThread.start();
handler = new Handler(handlerThread.getLooper());
}
#Override
public void schedule(long delay) {
handler.postDelayed(this, delay);
}
#Override
long nextTime() {
long nextTime = super.nextTime();
if(nextTime() > killThreadAt) {
handlerThread.quit();
}
return nextTime;
}
};
theJob.schedule(1500);
}
}

I had some similar issues .. I was trying to schedule different jobs at different rates and I found using the Quartz Scheduler library to handle all my scheduling problems a real relieve :)
For your problem: firing a job at a non-even rate, you could easily implement a TriggerListener and on completion reschedule the same job at nextTime()
The Quartz Scheduler easily integrates with Spring, Maven and has handles for all kind of scenarios like misfired jobs or thread exceptions.
Simple example (from the docs)
SchedulerFactory sf = new StdSchedulerFactory();
Scheduler sched = sf.getScheduler();
// define the job and tie it to our HelloJob class
JobDetail job = newJob(HelloJob.class)
.withIdentity("job1", "group1")
.build();
// compute a time that is on the next round minute
int minutesInterval = nextTime();
// Trigger the job to run on the next round minute and repeat it forever
Trigger trigger = newTrigger()
.withIdentity("trigger1", "group1")
.withSchedule(
simpleSchedule()
.withIntervalInMinutes(minutesInterval)
.repeatForever()
)
.build();
// Tell quartz to schedule the job using our trigger
sched.scheduleJob(job, trigger);
sched.start();

Related

Which threading mechanism to use for tasks that enqueue other tasks?

I'm using a task that creates other tasks. Those tasks in turn may or may not create subsequent tasks. I don't know beforehand how many tasks will be created in total. At some point, no more tasks will be created, and all the task will finish.
When the last task is done, I must do some extra stuff.
Which threading mechanism should be used? I've read about CountDownLatch, Cyclic Barrier and Phaser but none seem to fit.
I've also tried using ExecutorService, but I've encountered some issues such as the inability to execute something at the end, and you can see my attempt below:
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
public class Issue {
public static void main(String[] args) throws InterruptedException {
var count = new AtomicInteger(1);
var executor = Executors.newFixedThreadPool(3);
class Task implements Runnable {
final int id = count.getAndIncrement();
#Override
public void run() {
try {
MILLISECONDS.sleep((long)(Math.random() * 1000L + 1000L));
} catch (InterruptedException e) {
// Do nothing
}
if (id < 5) {
executor.submit(new Task());
executor.submit(new Task());
}
System.out.println(id);
}
}
executor.execute(new Task());
executor.shutdown();
// executor.awaitTermination(20, TimeUnit.SECONDS);
System.out.println("Hello");
}
}
This outputs an exception because tasks are added after shutdown() is called, but the expected output would be akin to:
1
2
3
4
5
6
7
8
9
Hello
Which threading mechanism can help me do that?
It seems pretty tricky. If there is even a single task that's either in the queue or currently executing, then since you can't say whether or not it will spawn another task, you have no way to know how long it may run for. It may be the start of a chain of tasks that takes another 2 hours.
I think all the information you'd need to achieve this is encapsulated by the executor implementations. You need to know what's running and what's in the queue.
I think you're unfortunately looking at having to write your own executor. It needn't be complicated and it doesn't have to conform to the JDK's interfaces if you don't want it to. Just something that maintains a thread pool and a queue of tasks. Add the ability to attach listeners to the executor. When the queue is empty and there are no actively executing tasks then you can notify the listeners.
Here's a quick code sketch.
class MyExecutor
{
private final AtomicLong taskId = new AtomicLong();
private final Map<Long, Runnable> idToQueuedTask = new ConcurrentHashMap<>();
private final AtomicLong runningTasks = new AtomicLong();
private final ExecutorService delegate = Executors.newFixedThreadPool(3);
public void submit(Runnable task) {
long id = taskId.incrementAndGet();
final Runnable wrapped = () -> {
taskStarted(id);
try {
task.run();
}
finally {
taskEnded();
}
};
idToQueuedTask.put(id, wrapped);
delegate.submit(wrapped);
}
private void taskStarted(long id) {
idToQueuedTask.remove(id);
runningTasks.incrementAndGet();
}
private void taskEnded() {
final long numRunning = runningTasks.decrementAndGet();
if (numRunning == 0 && idToQueuedTask.isEmpty()) {
System.out.println("Done, time to notify listeners");
}
}
public static void main(String[] args) {
MyExecutor executor = new MyExecutor();
executor.submit(() -> {
System.out.println("Parent task");
try {
Thread.sleep(1000);
}
catch (Exception e) {}
executor.submit(() -> {
System.out.println("Child task");
});
});
}
}
If you change your ExecutorService to this:
ThreadPoolExecutor executor = (ThreadPoolExecutor) Executors.newFixedThreadPool(3);
You could then use the count functions to wait:
while(executor.getTaskCount() > executor.getCompletedTaskCount())
{
TimeUnit.SECONDS.sleep(10L);
}
executor.shutdown();
System.out.println("Hello");

ScheduledExecutorService change delay repeatedly

I have a task I want to run where the delay will keep changing (i.e. - no fixed interval).
I want a thread and loops infinitely that finds the delay value, inputs this into the executor and then once that task is done find the new delay value etc..
My current 'basic' implementation:
long targetTime = findNextTime()/1000;
long currentTime = System.curentTimeMillis()/1000;
delay = targetTime - currentTime;
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(3);
Runnable task = new Runnable() {
public void run() {
System.out.println("Test Task");
ScheduledFuture<?> future = scheduler.schedule(this, delay, TimeUnit.SECONDS);
}
}
How can I get it to find the new delay and keep repeating this task?
Should I poll for future to be done and then do a recursive loop? I try this and I get an error.
There are some answer to resolve this.Reinitialize fix delay in ScheduledExecutorService and
How to change Spring's #Scheduled fixedDelay at runtime
here i support another method to realize this-repeated submit task to ExecutorService
class Task implements Runnable {
private static Random random = new Random();
private ExecutorService executor;
public Task(ExecutorService executor) {
this.executor = executor;
}
#Override
public void run() {
try {
System.out.println(System.currentTimeMillis() + " Hello World");
TimeUnit.MILLISECONDS.sleep(random.nextInt() % 100);
executor.execute(this);
} catch (Exception e) {
}
}
}

Cancel a scheduled fixed rate task depending on the result of the task

I'm using Spring's TaskScheduler to schedule a periodic task.
ScheduledFuture scheduleAtFixedRate(Runnable task, long period);
I understand that I can call cancel() on the ScheduledFuture to stop the recurring task from being executed. But I'd like to cancel the recurring scheduled task depending on the result of the execution of the task, and am not sure how to best do that.
Does the ScheduledFuture give me access to the result of EACH executed task? Or do I need some sort of task listener that can keep a reference to this ScheduledFuture, and cancel it that way? Or something else?
Ok it looks like it is possible, but there is probably a better approach.
Since a recurring job only takes a Runnable (with a void return type) there is no way to return the result of the task. So the only way to stop the recurring task is to make the task perform a side-effect, e.g. adding a stop message to a queue. Then a separate thread would need to monitor this queue, and it could cancel the job once it sees the message.
Very messy and complicated.
A better alternative is to create a normal (one time) scheduled task. The task itself can then decide whether or not it needs to schedule another task, and can do the scheduling of the next task itself.
Keep a handle or the original fixed rate ScheduledFuture, then when the condition arises where you want to cancel it, schedule a new task that does the cancel.
You might also be able to do something with a RunnableScheduledFuture.
From the ScheduledExecutorService docs
https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html
import static java.util.concurrent.TimeUnit.*;
class BeeperControl {
private final ScheduledExecutorService scheduler =
Executors.newScheduledThreadPool(1);
public void beepForAnHour() {
final Runnable beeper = new Runnable() {
public void run() { System.out.println("beep"); }
};
final ScheduledFuture<?> beeperHandle =
scheduler.scheduleAtFixedRate(beeper, 10, 10, SECONDS);
scheduler.schedule(new Runnable() {
public void run() { beeperHandle.cancel(true); }
}, 60 * 60, SECONDS);
}
}
Here is a modified beeper example that demonstrates how to make a decision after EACH scheduled task. I used a latch so I could wrap it in a test case and assert the right thing happened (and of course to keep the test runner's thread from stopping). I also changed the intervals (it beeps every 10ms after an initial 10ms delay) so the test could be copied, pasted, and executed in a second as opposed to an hour.
import org.junit.Assert;
import org.junit.Test;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class BeeperTest {
class BeeperControl {
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, (runnable) -> {
Thread thread = new Thread(runnable);
thread.setName("MyAwesomeBeeperTestThread");
thread.setDaemon(true);
return thread;
});
public void beepTheNumberOfTimesIWant(CountDownLatch latch) {
long initialDelay = 10;
long frequency = 10;
TimeUnit unit = TimeUnit.MILLISECONDS;
final int numberOfTimesToBeep = 5;
AtomicInteger numberOfTimesIveBeeped = new AtomicInteger(0);
final ScheduledFuture[] beeperHandle = new ScheduledFuture[1];
beeperHandle[0] = scheduler.scheduleAtFixedRate(() -> {
if (numberOfTimesToBeep == numberOfTimesIveBeeped.get()) {
System.out.println("Let's get this done!");
latch.countDown();
beeperHandle[0].cancel(false);
}
else {
System.out.println("beep");
numberOfTimesIveBeeped.incrementAndGet();
}
}, initialDelay, frequency, unit);
}
}
#Test
public void beepPlease() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
BeeperControl control = new BeeperControl();
control.beepTheNumberOfTimesIWant(latch);
boolean completed = latch.await(1, TimeUnit.SECONDS);
Assert.assertTrue("Beeper should be able to finish beeping" +
"within allotted await time.", completed);
}
}

Why is a TimerTask not reusable with a new Timer object?

The Timer (java.util.Timer) doc describes the cancel method as one that affects the Timer and it states that the timer cannot be used after cancellation. So I instantiate a new Timer. Why will it not let me re-use the argument task0 in this example? I'm not even invoking purge which is described as making tasks GC-eligible. Until it might be explained to be otherwise, I claim Timer class should not affect a TimerTask object that is merely an argument to it.
import java.util.Timer;
import java.util.TimerTask;
public class Tester {
public static void main(String[] args) throws InterruptedException {
long delay = 3000L;
Timer timer0 = new Timer();
Task task0 = new Task();
timer0.schedule(task0, delay);
timer0.cancel();
Timer timer1 = new Timer();
timer1.schedule(task0, delay); // throws an exception if we use task0
Thread.sleep(5000);
timer1.cancel();
}
}
class Task extends TimerTask {
Task() {
}
#Override
public void run() {
System.out.println("task was invoked");
}
}
Allowing this would be error prone, since task0 could still be running when scheduled again by another timer. (Note that cancel() does not terminate the task.)
Note that if task0 is managed by a single Timer, the same task will never be executed concurrently with itself (regardless if it is executed with fixed-delay or with fixed-rate).
If you really want such behavior, the work around would be to let task0 and a task1 wrap a common object:
class Task extends TimerTask {
Runnable runnable;
Task(Runnable runnable) {
this.runnable = runnable;
}
#Override
public void run() {
runnable.run();
}
}
And then execute it like this:
// "Wrapped" (and thus shared) by task0 and task1 below.
Runnable runnable = new Runnable() {
#Override
public void run() {
System.out.println("task was invoked");
}
}
Timer timer0 = new Timer();
Task task0 = new Task(runnable);
timer0.schedule(task0, delay);
timer0.cancel();
Task task1 = new Task(runnable);
Timer timer1 = new Timer();
timer1.schedule(task1, delay); // throws an exception if we use task0
Thread.sleep(5000);
timer1.cancel();
Take a look:
http://www.docjar.com/html/api/java/util/TimerTask.java.html
http://www.docjar.com/html/api/java/util/Timer.java.html
The TimerTask class is just a thin extension of Runnable which tracks a bit of metadata about scheduling (namely: next execution time). But, if you schedule it on two timers, there's still only one next execution field, so one timer would overwrite the next execution time of the other, which is almost certainly not what you want, so it tracks that it's been scheduled before, and throws an exception in Timer, instead.
If it allowed this, you'd get rather unexpected behavior.
This also is a problem if a timer task wants to re-schedule itself in the same Timer - maybe with a different delay this time.
This would allow me for example to implement something like an exponential backoff algorithm (retry a task, with exponentially growing delays.
It seems such a timer task with variable delays can be most comfortably implemented by using a ScheduledExecutorService, because this class does not pose such restrictions.
Alternatively, you can create a method to return the same task and then schedulde it to the same timer or different timers:
import java.util.Timer;
import java.util.TimerTask;
public class Tester {
public static void main(String[] args) throws InterruptedException {
long delay = 3000L;
Timer timer0 = new Timer();
timer0.schedule(createTask(), delay);
Timer timer1 = new Timer();
timer1.schedule(createTask(), delay);
Thread.sleep(5000);
timer1.cancel();
// purge all canceled tasks from timers
timer0.purge();
timer1.purge();
}
}
Task createTask() {
Task task0 = new Task();
return task0;
}
class Task extends TimerTask {
Task() {
}
#Override
public void run() {
System.out.println("task was invoked");
// here do something
cancel(); // stop it.
}
}

Java Timer

I'm trying to use a timer to schedule a recurring event in an application. However, I want to be able to adjust the period at which the event fires in real time (according to the users input).
For example:
public class HelperTimer extends TimerTask
{
private Timer timer;
//Default of 15 second between updates
private int secondsToDelay = 15;
public void setPeriod(int seconds)
{
this.secondsToDelay = seconds;
long delay = 1000; // 1 second
long period = 1000*secondsToDelay; // seconds
if (timer != null)
{
timer.cancel();
}
System.out.println(timer);
timer = new Timer();
System.out.println(timer);
timer.schedule(this, delay, period);
}
public int getPeriod()
{
return this.secondsToDelay;
}
}
I then start a new instance of this class and call its set period function. However, when I do that, I get an Illegal state exception. You can see the System.out.println(timer); in there because I'm checking, and yep sure enough, they are two different timers... so why am I getting an IllegalStateException when I try to run a schedule call on a brand new Timer instance!?!?!?!
java.util.Timer#c55e36
java.util.Timer#9664a1
Exception in thread "AWT-EventQueue-0" java.lang.IllegalStateException: Task already scheduled or cancelled
at java.util.Timer.sched(Unknown Source)
at java.util.Timer.schedule(Unknown Source)
at HelperTimer.setPeriod(HelperTimer.java:38)
You can't reuse a TimerTask as you're doing here.
Relevant porition of Timer:
private void sched(TimerTask task, long time, long period) {
if (time < 0)
throw new IllegalArgumentException("Illegal execution time.");
synchronized(queue) {
if (!thread.newTasksMayBeScheduled)
throw new IllegalStateException("Timer already cancelled.");
synchronized(task.lock) {
//Right here's your problem.
// state is package-private, declared in TimerTask
if (task.state != TimerTask.VIRGIN)
throw new IllegalStateException(
"Task already scheduled or cancelled");
task.nextExecutionTime = time;
task.period = period;
task.state = TimerTask.SCHEDULED;
}
queue.add(task);
if (queue.getMin() == task)
queue.notify();
}
}
You'll need to refactor your code so that you create a new TimerTask, rather than re-using one.
It seems odd to me to have a TimerTask with its own Timer inside it. Bad design. I'd totally separate the two and have the TimerTask implementation be handed off to a Timer, and put all that logic about fiddling with the period inside another class that provides an interface for doing so. Let that class instantiate the Timer and TimerTask and send them off to do their work.
You can use ScheduledExecutorService, which allows you to schedule the same task multiple times without using scheduleAtFixedRate. Here's a quick example:
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
Runnable timerTask = new Runnable() {
#Override
public void run() {
// Do something
System.out.println("Task run!");
// Schedule again
executorService.schedule(this, 15, TimeUnit.SECONDS);
}
};
// Schedule
executorService.schedule(timerTask, 15, TimeUnit.SECONDS);
In this exmaple, "Executed...." will be printed after 4 seconds of delay. After that, it will be printed continuously every 3 seconds:
import java.util.*;
class TimeSetting {
public static void main(String[] args) {
Timer t = new Timer();
TimerTask time = new TimerTask() {
public void run() {
System.out.println("Executed......");
}
};
t.scheduleAtFixedRate(time, 4000, 3000);
/*
* The task will be started after 4 secs and
* for every 3 seconds the task will be continuously
* executed.....
*/
}
}

Categories

Resources