ScheduledExecutorService and ThreadPoolTaskExecutor that interrupts tasks after a timeout - java

I Used ExecutorService that interrupts tasks after a timeout.I use a ScheduledExecutorService for this. First I submitted the thread and it once to begin immediately and retain the future that is created. After that i use ScheduledExecutorService as a new task that would cancel the retained future after some period of time.
//Start Spring executor to submit tasks
ThreadPoolTaskExecutor taskExecutor = (ThreadPoolTaskExecutor) ApplicationContextProvider.getApplicationContext().getBean("taskExecutor");
CompletionService completionService = new ExecutorCompletionService(taskExecutor);
//End Spring executor to submit tasks
// Start ScheduledExecutorService to submit returned future object to timeout
ScheduledExecutorService executor = Executors.newScheduledThreadPool(Integer.parseInt(config.getProperty("DBPOLLER_COREPOOLSIZE")));
final Future<String> future = completionService.submit(batchJob); // Submit actual task and get future
// submit future
executor.schedule(new Runnable() {
public void run() {
future.cancel(true);
}
}, dbPollerTimeOut, TimeUnit.MINUTES);
int count = taskExecutor.getActiveCount();
if (count == 0) {
taskExecutor.shutdown();
executor.shutdown();
finalExitStatus = 0;
break;
}
I have implemented the solution which is in below url:
ExecutorService that interrupts tasks after a timeout, it was working fine, until timeout, but once timeout happens, it cancels all theenter code here tasks i ThreadPool which is not acceptable. I need to cancel only tasks that are long running and reach timeout.
Any idea how to achieve this?

It is not clear what your CompletionService is, and you are submitting your batchJob on it, so it is hard to tell exact root cause of your problem. But ideal scenario of submitting few tasks and cancelling them after some time, is to use ScheduledExecutorService for both purposes.
So, can try submitting the batchJobon instance of ScheduledExecutorService i.e. executor.
final Future<String> future = executor.submit(batchJob); // Submit actual task and get future
EDIT UPDATE: Important change you SHOULD do in your code
I see that you are never stopping your ScheduledExecutorService which is wrong because resources it occupies will never be released until you stop it. So, your updated code should be as below:
ScheduledExecutorService executor = Executors.newScheduledThreadPool(Integer.parseInt(config.getProperty("DBPOLLER_COREPOOLSIZE")));
final Future<String> future = executor.submit(batchJob); // Submit actual task and get future
executor.schedule(new Runnable() {
public void run() {
future.cancel(true);
executor.shutdownNow();
}
}, dbPollerTimeOut, TimeUnit.MINUTES);

Related

How to delay a scheduled task with Spring?

I'd like to create a method that delays the execution on method invocation by 60s.
Problem: if the method is called within that 60s, I want it to be delayed again by 60s from last point of invocation. If then not called within 60s, the execution may continue.
I started as follows, but of course this is only a one-time delay:
public void sendDelayed(String info) {
//TODO create a task is delayed by +60s for each method invocation
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
executorService.schedule(Classname::someTask, 60, TimeUnit.SECONDS);
}
How could I further delay the execution on each invocation?
executorService.schedule returns a ScheduledFuture which provides a cancel method to cancel its execution. cancel takes a single boolean parameter mayInterruptIfRunning which, when set to false, will only cancel the execution if the task has not started yet. See also the docs.
Using this you could do something like this:
private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
private ScheduledFuture<?> future;
public void sendDelayed(String info) {
// When there is a task that has not been started yet, stop it:
if (future != null) {
boolean cancelled = future.cancel(false);
if (cancelled) {
logger.debug("Task has been cancelled before execution");
} else {
logger.debug("Task is already running or has been completed");
}
}
// Old task has been cancelled or already started - schedule a new task
future = executorService.schedule(Classname::someTask, 60, TimeUnit.SECONDS);
}
You may have to take care of avoiding race conditions regarding concurrent access to the future field though.

Java how to timeout a thread WITHOUT using future.get and without blocking parent thread

I am looking for ways to time out a thread execution and found below example: https://stackoverflow.com/a/16231834/10015830
Future<?> future = service.submit(new MyCallable());
try {
future.get(100, TimeUnit.MILLISECONDS);
} catch (Exception e){
e.printStackTrace();
future.cancel(true); //this method will stop the running underlying task
}
But my need is different from above example: I do not want the parent thread to be blocked at future.get. In other words, I do not need to get the result of my callable. Because in my actual application, the parent thread is executed periodically (a scheduled task, say, 5 sec periodically).
Is there a way timeout a thread WITHOUT using future.get and without blocking parent thread? (It seems invokeAll is also blocking).
You can cancel long running task from a timer task:
import java.util.Timer;
import java.util.TimerTask;
Timer timer = new Timer();
Future<?> future = service.submit(new MyCallable());
TimerTask controlTask = new TimerTask(){
#Override
public void run() {
if (!future.isDone()) {
future.cancel(true);
}
}
};
long delay = 100;
timer.schedule(task, delay);

How to timeout a newSingleThreadScheduledExecutor

In Android, I'm trying to start a thread that kills itself or is canceled by the executor after some timeout limit for example 20 seconds? So the runnable would only do its work for that many seconds than cancel...
How do I achieve this? I'm currently starting it as the following.
Executors.newSingleThreadScheduledExecutor().schedule(myRunnable, 0, TimeUnit.MILLISECONDS);
My runnable looks like this
static class MyRunnable implements Runnable {
MyRunnable(Helper _helper) {
helper = _helper;
}
public void run() {
}
}
If you want to stop a task that has already been submitted to an ExecutorService then you'll need to deal with Future objects.
Like this answer:
Stop a Runnable submitted to ExecutorService
Basically you will submit a Runnable and get back a Future object that you can use to cancel it.
Specific to your concern: Stop one task in 20 seconds you could use handoff the Future object from the first task to another like so:
ExecutorService executor = Executors.newSingleThreadScheduledExecutor();
// Schedule your audio processing job like normal.
final Future<?> future = executor.submit(new MyAudioProcessingRunnable())
// Schedule the cancelation to happen in 20 seconds.
executor.schedule(new Runnable() {
if (!future.isCancelled() && !future.isDone()) {
// Cancel and interrupt any blocking calls.
future.cancel(true);
}
}, 20, TimeUnit.Seconds);
IMPORTANT - You must have code in your MyAudioProcessingRunnable to support cancelation.

How to shutdown CompletionService after completing currently executed tasks

I have something like this:
ExecutorService executor = Executors.newFixedThreadPool(2);
CompletionService<Boolean> completionService = new ExecutorCompletionService<>(executor);
int i = 0;
while (i < 40) {
completionService.submit(getTask());
i++;
}
executor.shutdown();
System.out.println("SHUTDOWN");
After calling shutdown all submitted tasks are executed. If I call shutdownNow, then currently executed threads are throws java.lang.InterruptedException.
Is there are any way to wait currently executed tasks to complete and don't execute other submitted tasks?
shutdown() allows the currently submitted tasks to complete, but rejects new ones:
Initiates an orderly shutdown in which previously submitted tasks are executed, but no new tasks will be accepted.
If you want to wait in your main thread for the executor to shut down, you can invoke executor.awaitTermination(long timeout, TimeUnit unit):
Blocks until all tasks have completed execution after a shutdown request, or the timeout occurs, or the current thread is interrupted, whichever happens first.
If you want to allow the tasks that are currently running to complete, but discard the ones that are already submitted to the queue, you have a few choices:
cancelling the Futures with cancel(false):
Attempts to cancel execution of this task. This attempt will fail if the task has already completed, has already been cancelled, or could not be cancelled for some other reason. If successful, and this task has not started when cancel is called, this task should never run.
Returns:
false if the task could not be cancelled, typically because it has already completed normally; true otherwise
wrapping your Runnable/Callable with a custom CancellableRunnable/Callable (depending on what your getTask() returns):
class CancellableRunnable implements Runnable {
private final AtomicBoolean shouldRun;
private final Runnable delegate;
public CancellableRunnable(AtomicBoolean shouldRun, Runnable delegate) {
this.shouldRun = shouldRun;
this.delegate = delegate;
}
#Override
public void run() {
if (shouldRun.get()) {
delegate.run();
}
}
}
and the usage in your example:
AtomicBoolean shouldRun = new AtomicBoolean(true);
while (i < 40) {
completionService.submit(new CancellableRunnable(shouldRun, getTask()));
i++;
}
shouldRun.set(false);
executor.shutdown();
Yes, after you have called shutdown(), the executor will accept no new tasks. Next you call awaitTermination() to await running tasks completing.
If all you want is the first two results and then discard the other tasks, you can wait for the first two tasks to be completed then cancel the others, for example by calling shutdownNow if you don't need the completion service any longer.
Future<Boolean> result1 = copmletionService.take();
Future<Boolean> result2 = copmletionService.take();
completionService.shutdownNow();

Waiting in main Thread till a canceled Executor Task is finished

i have a ScheduledExecutorService (with newScheduledThreadPool( 1)) where i add tasks like this:
myTask = scheduler.scheduleAtFixedRate(new Runnable() {
#Override public void run() {
// do work
}
},
delay,
interval,
TimeUnit.MILLISECONDS );
I have a list of all my tasks. If i for example want to remove a task i can do this (e.g. the first task)
myTaskList.get(0).getTask().cancel(true);
My problem now is that if the task i want to cancel is currently running in the scheduler thread i have to wait in the main-thread till the task is canceled. I thought i could do this with get() like this:
myTaskList.get(0).getTask().cancel(true);
myTaskList.get(0).getTask().get();
but my main-thread goes on without waiting for the task code to finish.
For clarity, what i want is basically this:
User wants to cancel task
If to be canceled Task is the one currently running in the scheduler thread the main-thread have to wait till the task is no longer the one executing in the scheduler thread
If I truly understood your mean, you want main method waits for child thread to finish/stop. Is it true? If so, You can use method join() in the main method. This method forces main method to wait for child thread to stop/finish. This method is in class java.lang.Thread.
I don't think this would work in practice. When you attempt a get() on a canceled Future it will throw a CancellationException as per API
#throws CancellationException - if the computation was cancelled
What you could try is to map the Future with a CountdownLatch maybe something like this
class LatchedFuture{
volatile Future future;
final CountdownLatch latch = new CountdownLatch(1);
}
final LatchedFuture latchedFuture = new LatchedFuture();
latchedFuture.future = scheduler.scheduleAtFixedRate(new Runnable() {
#Override public void run() {
// do work
latchedFuture.latch.countDown();
}
},
delay,
interval,
TimeUnit.MILLISECONDS );
Then when you want it to finish.
myTaskList.get(0).getTask().future.cancel(true);
myTaskList.get(0).getTask().latch.await();

Categories