ScheduledExecutorService in an Android activity - java

private ScheduledFuture<?> future;
private ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
Runnable runnable = new Runnable() {
#Override
public void run() {
//my code...
}
};
future = scheduler.scheduleAtFixedRate(runnable, 0, 15,TimeUnit.SECONDS);
I have the aforementioned code in an android activity. So, I press a button and I go to the next activity. If I press the back button or explicitely (from a button) go to the previous activity again, is there any posibility a second timer to be created?
In other words, does the Executors.newScheduledThreadPool(1) verifies me that the runnable will be executed only by one thread and that I will not have for example, two different tasks that both of them will run every 15 seconds?

From documentation http://developer.android.com/reference/java/util/concurrent/ScheduledThreadPoolExecutor.html
public ScheduledFuture<?> scheduleAtFixedRate (Runnable command, long initialDelay, long period, TimeUnit unit)
Added in API level 1
Creates and executes a periodic action that becomes enabled first after the given initial delay, and subsequently with the given period; that is executions will commence after initialDelay then initialDelay+period, then initialDelay + 2 * period, and so on. 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. If any execution of this task takes longer than its period, then subsequent executions may start late, but will not concurrently execute.
So, AFAIU the size of pool 1 and scheduleAtFixedRate ensure that there is only one thread running.

I think that even if you switch Activities, your Runnable will keep running, since the activity context will be saved by the OS.
You should stop your thread when the Activity's onPause or onStop are called, by using:
future.cancel(true);
Then, if you come back to the activity you can schedule the Runnable again.

Related

Behavior of ScheduledExecutorService

I've been wondering about specific case around ScheduledExecutorService in java.
Let,
ScheduledExecutorService = new ScheduledThreadPoolExecutor(2);
service.scheduleAtFixedRate(new Runnable() {
#Override
public void run() {
//Some task taking longer than schedule period to finish executing.
}
},initialDelay,period,TimeUnit.SECONDS);
in this case, say period is 4 seconds. When the schedular starts to execute after initialdelay, task will be blocked inside while(true) inifinite loop.
My question is after each 4 seconds does a task get scheduled disregarding the execution(unfinished) of previous round of task? Because if this is the case this code will crash eventually after running out of memory.
Help is appreciated.
Thank you.
From the javadoc of scheduleAtFixedRate():
If any execution of this task takes longer than its period, then
subsequent executions may start late, but will not concurrently
execute.
Meaning the task will be started once, but since it never finishes there won't be other invocations. You'll just be wasting one thread in the pool.
The same applies to scheduleWithFixedDelay() since the delay is counted from the time when the previous execution finishes (and since it doesn't finish, no next execution can happen).

repetitve timer doesnt work when looper is inside the runnable

I am testing a few things with TimerTask and Timers and android , and i noticed that if i put Looper inside the run () method, the TimerTask runs just once, even though i defined it to be repetitive.Any ideas why is that ?
here is the MainActivity part
Timer timi=new Timer();
timi.scheduleAtFixedRate(new locac(nok,this),10, 1000);
and here is the worker timerTask class
public void run ()
{
Looper.prepare();
int loto=23;
int lato=23;
long time=1220227200;
String test=String.valueOf(lato);
String test3=String.valueOf(loto);
String test1=String.valueOf(time);
dbadapter mkola=new dbadapter(Ctx);
mkola.openToWrite();
mkola.insert(test,test1,test3);
Looper.loop();
}
as soon as i remove the Looper , it works nice.
i need the Looper because at a point i want to invoke some methods inside which initiate a Handler
thanks in advance
It is because your TimrTask never returns (because of the call to Looper.loop() )
Form The Timertask documentation, emphasis mine (Oracle Documentation, Android's is not that clear) http://docs.oracle.com/javase/7/docs/api/java/util/Timer.html
:
Corresponding to each Timer object is a single background thread that
is used to execute all of the timer's tasks, sequentially. Timer tasks
should complete quickly. If a timer task takes excessive time to
complete, it "hogs" the timer's task execution thread. This can, in
turn, delay the execution of subsequent tasks, which may "bunch up"
and execute in rapid succession when (and if) the offending task
finally completes.
So what happens is that your TimerTask is run in the single thread of your Timer and as it's run method never returns it bloks the thread, which cannot run the new scheduled itereation of your TimerTask anymore.

Increment sleep/wait time until required

I'm not very good in multi-threading, this might be a basic question. But i have'nt been able to find an answer.
Scenario:
Lets say I have an event listener which is fired by something. Every time the event is fired, i want to start a new thread which takes about 3 seconds to execute.
Problem:
The problem is that the event can be fired more than once in a second, and I don't want to start multiple threads at once.
Requirement:
How can I schedule a thread lets say at 1000ms after the event. If the event keeps on firing, i want to keep delaying the scheduled time of thread. This way my thread executes after 1000ms of the last time the event was fired.
The event listener, on start up, creates and starts a new private thread. The thread contain a list of tasks to do and executes them one at a time sequentially. Each time the even listener receives a new event, it creates a new task, and adds it to the list of tasks in the private thread.
EDIT: Eugene suggested using a Thread Pool, which might be beneficial in your case if you have a great amount of work to do with each task taking up considerable amount of time. Take a look at thread pools in Java API, like this: http://docs.oracle.com/javase/tutorial/essential/concurrency/pools.html
I would use a ScheduledExecutorService - schedule the task to happen in one second and if there is a task already scheduled cancel it and schedule a new one to happen in one second
This way your task will execute one second after the last time the event was triggered.
private class Task implements Runnable {
#Override
public void run() {
throw new UnsupportedOperationException("Not supported yet."); //To change body of generated methods, choose Tools | Templates.
}
}
private final ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
private Future<?> scheduledTaskHandle;
private class Listener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (scheduledTaskHandle != null && !scheduledTaskHandle.isDone()) {
scheduledTaskHandle.cancel(false);
}
scheduledTaskHandle = executorService.schedule(new Task(), 1, TimeUnit.SECONDS);
}
}
The Task is a Runnable that does your long-running operation. the Listener is your listener class.
In the Listener.actionPerformed method we first check whether a task is already scheduled by using the Future, if it is we cancel it. We don't need to worry about race hazards here is if the tasks finishes in between the call to isDone and the call to cancel then nothing will happen.
If the task is running at the point when the Listener fires then that task will complete, as the cancel method is called with false. Another task will be scheduled to run one second after the firing of the listener or once the currently running task has completed (as we are only using a single thread no more than one task can run).
The Listener will then schedule a new execution of the task to happen in one second.

How to remove a task from ScheduledExecutorService?

I have a ScheduledExecutorService that times a few different task periodically with scheduleAtFixedRate(Runnable, INIT_DELAY, ACTION_DELAY, TimeUnit.SECONDS);
I also have a different Runnable that I'm using with this scheduler.
the problem starts when I want to remove one of the tasks from the scheduler.
Is there a way to do this?
Am I doing the right thing using one scheduler for different tasks?
What is the best way to implement this?
Simply cancel the future returned by scheduledAtFixedRate():
// Create the scheduler
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
// Create the task to execute
Runnable r = new Runnable() {
#Override
public void run() {
System.out.println("Hello");
}
};
// Schedule the task such that it will be executed every second
ScheduledFuture<?> scheduledFuture =
scheduledExecutorService.scheduleAtFixedRate(r, 1L, 1L, TimeUnit.SECONDS);
// Wait 5 seconds
Thread.sleep(5000L);
// Cancel the task
scheduledFuture.cancel(false);
Another thing to note is that cancel does not remove the task from scheduler. All it ensures is that isDone method always return true. This may lead to memory leaks if you keep adding such tasks. For e.g.: if you start a task based on some client activity or UI button click, repeat it n-times and exit. If that button is clicked too many times, you might end up with big pool of threads that cannot be garbage collected as scheduler still has a reference.
You may want to use setRemoveOnCancelPolicy(true) in ScheduledThreadPoolExecutor class available in Java 7 onwards. For backward compatibility, default is set to false.
If your ScheduledExecutorService instance extends ThreadPoolExecutor (e.g. ScheduledThreadPoolExecutor), you could use remove(Runnable) (but see the note in its javadoc: "It may fail to remove tasks that have been converted into other forms before being placed on the internal queue.") or purge().

Schedule a single-threaded repeating runnable in java, but skip the current run if previous run is not finished

Sometimes the duration of a repeated task is longer than its period (In my case, this can happen for hours at a time). Think of a repeated task that takes 7 minutes to run and is scheduled to run every 10 minutes, but sometimes takes 15 minutes for each run for a few hours in a row.
The Timer and ScheduledThreadPoolExecutor classes both have a scheduleAtFixedRate method that is usually used for this type of functionality. However, both have the characteristic that they 'try to catch up when they fall behind'. In other words, if a Timer falls behind by a few executions, it builds up a queue of work that will be worked on continuously until it catches back up to the number of runs that would have happened if none of the tasks had taken longer than the specified period. I want to avoid this behavior by skipping the current execution if the previous run is not complete.
I have one solution that involves messing around with the afterExecution method of a pooled executor, recalculating a delay, and rescheduling the runnable with the new delay, but was wondering if there's a simpler way, or if this functionality already exists in a common library somewhere. I know about scheduling with a fixed delay rather than a fixed period, but this will not work for me since it's important to try to execute the tasks at their fixed times. Are there any simpler options than my afterExecution solution?
I think what you want is for the long-running task itself to not run in the ScheduledExecutorService itself, but in a background thread. Then the fixed-rate task will always complete quickly, since it is only used for checking whether to start the actual task in the background (or not, if it's still running from last time).
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
final Runnable actualTask = null;
executorService.scheduleAtFixedRate(new Runnable() {
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private Future<?> lastExecution;
#Override
public void run() {
if (lastExecution != null && !lastExecution.isDone()) {
return;
}
lastExecution = executor.submit(actualTask);
}
}, 10, 10, TimeUnit.MINUTES);
You could use scheduleWithFixedDelay method instead. It's similar but this one does not have a queue for missed runs and instead starts counting again only when the current Runnable was terminated.
The documentation states the reexecution of the Runnable will be scheduled based on the delay parameter:
The delay between the termination of one execution and the commencement of the next.
Make a third class, say called Coordinator. Coordinator has a synchronized startRunning() method which sets isRunning to true and returns true if another thread was not running already. There should also be a synchronized stopRunning method which sets isRunning to false. It returns true if a runnable is already running. You make a single instance of this class and pass a reference to all of the runnables you construct. In the runnable's run method you first call startRunning and check the return to verify that another one isn't running already. Make sure to put the code in run() in a try-finally and call stopRunning from within the finally block.

Categories