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.
Related
I have an ExecutorService that I submit tasks to:
private final ExecutorService CUSTOM_POOL = Executors
.newCachedThreadPool();
private Queue<Future<?>> customTasksHandles;
private boolean started;
private Runnable task = new Runnable() {
#Override
public void run() {
if (!started) {return;}
...
customTasksHandles.add(CUSTOM_POOL.submit(new CustomTask(customData)));
...
}
}
I need to create public void stop() and public void start() functions. The stop() function would future.cancel() for every task that has been submitted to the executor, while the start() would start running the task Runnable again.
public void stop() {
...
Future customTasksHandle= customTasksHandles.poll();
while (customTasksHandle!=null) {
customTasksHandle.cancel(true);
customTasksHandle=locationTasksHandles.poll();
}
...
started = false;
}
public void start() {started = true;}
I tried to just CUSTOM_POOL.shutdown(), however it seems to make it impossible to submit new tasks later, after the start() is called. It seems that the only way is to loop over all submitted tasks and call .cancel() on each.
However, how do I get all submitted tasks without adding each task to a list/queue when submitting? Is there a better way to do it other than the way above? I was hoping for a List<Future> submittedTasks = CUSTOM_POOL.getAllSubmittedTasks() method of sorts, but there doesn't seem to be one. Interestingly, .shutdown() and invoke() methods do return List<Future>, but have side-effects.
As you can see here you could use the shutdownNow() method to stop and retrieve all the task that where waiting for execution. If what you want is just stop ("pause") the procesing of the task and the continue with it, you migth want to keep track yourself of the status of the taks and when you pause and unapuse the task you can resubmit the task returned by the mehtod shutdownNow() and the one that where executing in the instant of the stop. You should take into account that to stop the threads the pool may call thread interrupt so, if you are executing some sensible work you should take care of it properly. There is no pause and unpause for threads. check this
You can achieve this by using Future, create start method which accepts Runnable and return Future
public Future<?> start(Runnable run) {
return CUSTOM_POOL.submit(run);
}
You can save all these Future in a List or Map so that you can cancel which ever you need by using custom stop method
public void stop(Future<?> future) {
future.cancel(true);
}
Example
public class TestMain {
private final ExecutorService CUSTOM_POOL = Executors
.newCachedThreadPool();
public static void main(String[] args) {
//custom logic
}
public Future<?> start(Runnable run) {
return CUSTOM_POOL.submit(run);
}
public void stop(Future<?> future) {
future.cancel(true);
}
}
Future
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. If the task has already started, then the mayInterruptIfRunning parameter determines whether the thread executing this task should be interrupted in an attempt to stop the task.
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();
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);
I am confused with the following.
I know, if I use the schedule method from the ScheduledThreadPoolExecutor class:
ScheduledFuture<?> scheduledFuture =
scheduledThreadPoolExecutor.schedule(myClassRunnable, 5, TimeUnit.SECONDS);
I am able to retrieve later the value through
scheduledFuture.get(5, TimeUnit.SECONDS) or scheduledFuture.get() and it should be null because the task has been executed just only once and it is completed.
And null because I am working with the Runnable schedule method version and not with the Callable schedule method version. It according with the API.
Until here I am fine.
My question:
What is the purpose of ScheduledFuture if is retrieved from the scheduleWithFixedDelay (even from scheduleAtFixedRate) method:
ScheduledFuture<?> scheduledFuture=
scheduledThreadPoolExecutor.scheduleWithFixedDelay(myClassRunnable, 1, 5, TimeUnit.SECONDS);
Yes, I know both fixed methods execute the same task many times until the ScheduledThreadPoolExecutor's shutdown method is called (it must stop all the tasks scheduled).
I did a research through Google looking for some examples using ScheduledFuture returned from scheduleWithFixedDelay, I only found one using the cancel method, to cancel a specific task. But none working with get().
I don't know if I am wrong, but seems useless the get() method if we are working with scheduleWithFixedDelay, because if I use later:
scheduledFuture.get() - it remains awaiting and the Runnable object remains working many times (run,complete,delay,run,etc... )
scheduledFuture.get(32, TimeUnit.SECONDS) - always gives a TimeoutException
I thought I should be able to retrieve the null value since I can use the period argument/parameter from the scheduleWithFixedDelay method. I.e: Run the Runnable object, wait until it completes and use the scheduledFuture.get() to get the null value that confirms it has been completed, await the period of the delay time to run again the Runnable object according with the period value etc....
Clarifications and examples are totally welcome.
ScheduledFuture can be used to get time left before next task execution:
ScheduledFuture<?> f = Executors.newScheduledThreadPool(1).scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("run");
}
}, 0, 10000, TimeUnit.MILLISECONDS);
Thread.sleep(1000);
System.out.println("Time left before next run " + f.getDelay(TimeUnit.MILLISECONDS));
prints
run
Time left before next run 8999
I tried this out with the following code:
ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
AtomicInteger count = new AtomicInteger(0);
Runnable task = () -> {
int currentCount = count.incrementAndGet();
System.out.println("Task #" + currentCount + " started");
if (currentCount == 2) {
System.out.println("Shutting down scheduler...");
scheduler.shutdown();
}
try {
Thread.sleep(1000);
} catch (InterruptedException ie) {
throw new RuntimeException(ie);
}
System.out.println("Task #" + currentCount + " finished");
};
System.out.println("Starting scheduler...");
ScheduledFuture<?> scheduledFuture = scheduler.scheduleWithFixedDelay(
task, 0, 2, TimeUnit.SECONDS);
System.out.println("Getting scheduled future...");
System.out.println(scheduledFuture.get());
System.out.println("End of code reached.");
Here is the output:
Exception in thread "main" java.util.concurrent.CancellationException
at java.base/java.util.concurrent.FutureTask.report(FutureTask.java:121)
at java.base/java.util.concurrent.FutureTask.get(FutureTask.java:191)
at Rextester.main(source.java:33)
Starting scheduler...
Getting scheduled future...
Task #1 started
Task #1 finished
Task #2 started
Shutting down scheduler...
Task #2 finished
Online Rextester Demo: https://rextester.com/LYKN32123
Not sure how useful this is but it shows the get() method throws a CancellationException if the scheduler is shut down.
I would think that in the case of using .scheduleWithFixedDelay(...) (or scheduleAtFixedRate(...)), the get() method of the returned ScheduledFuture<?> feels indeed as an odd fit.
I believe you won't ever receive anything from the get() method, just expect an exception to be thrown from it, when Runnable is cancelled.
Surely one can see a use-case for this ;-)
see JavaDoc
https://docs.oracle.com/javase/8/docs/api/java/util/concurrent/ScheduledExecutorService.html#scheduleAtFixedRate-java.lang.Runnable-long-long-java.util.concurrent.TimeUnit-
Returns:
a ScheduledFuture representing pending completion of the task, and whose get() method will throw an exception upon cancellation
You can't catch the exception thrown by the submitted task without calling get.
Suppose you have a task like this.
public class Task1 implements Runnable {
private int counter = 3;
#Override
public void run() {
System.out.println("It is called");
counter--;
if (counter == 0) {
throw new RuntimeException(new Exception("It fails"));
}
}
}
No exception is thrown by the following code, so you are not able to handle that.
public static void main(String[] args) throws Exception {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
Task1 task1 = new Task1();
executorService.scheduleAtFixedRate(task1, 0, 2, TimeUnit.SECONDS);
}
However, if you add get to it, then you can catch the exception.
public static void main(String[] args) throws Exception {
ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
Task1 task1 = new Task1();
ScheduledFuture<?> future = executorService.scheduleAtFixedRate(task1, 0, 2, TimeUnit.SECONDS);
try {
future.get(); // CAUTION: This line blocks the execution
} catch (ExecutionException e) {
e.printStackTrace();
executorService.shutdown();
}
}
Using scheduledFuture.get() you can get a handle to the task, and in case this task needs to be cancelled say manually from the UserInterface or based on some conditions like the null, the handle can be used to cancel it.
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();