Java Executors cancel job by timeout with inner executor - java

I have a big thread pool like
ThreadPoolExecutor pool = new ThreadPoolExecutor(cores, 50, 30L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(3000));
I want to submit a task there. But I don't want to call future.get() at all, I want to submit it and forget.
But, I want my task to be cancelled in a certain time period. So I do the following:
public void run(){
FutureTask<WorkerWrapper> task = new FutureTask<>(()->worker.process(tmpArg));
executor.execute(task);
res=task.get(2, TimeUnit.MINUTES);
executor.shutdown();
}
In other words, I call executor inside executor. Is it safe and stable? Or can I cancel my task by timeout other way?

The only real way to achieve this, AFAIK, is to have a second ScheduledExecutor. You'd still need a handle of the Future but only pass it to the scheduled executor for cancellation.
Future<WorkedWrapper> task = executor.execute(() -> worker.process(tmpArg));
scheduledExecutor.schedule(() -> task.cancel(true), 2, TimeUnit.MINUTES);
There may be a more fluent way of handling this with CompleteableFutures but I am not 100% sure of that.

ScheduledThreadPoolExecutor has a method that allows to to schedule a runnable and get a future back. You can combine this with the timed get on the future:
ScheduedThreadPoolExecutor e = ...
Future<T> f = e.submit(new Runnable(){..},new T(){});
Object ignore = f.get(timeout, timeunit);

Related

Can a nested ExecutorService run in parallel

Edit: Changed the title and reformatted my code: (Pleas bear with my questions, i am new to this parallel world)
Instead of using FJPool, i am using ExecutorService now to parallelize my tasks. All tasks and subtasks are independent and can run in parallel without any conflict. Here is my code snippet:
ExecutorService executor = Executors.newFixedThreadPool(2);
executor.submit(() -> someTask(args1);
executor.submit(() -> someTask(args2);
// Wait for tasks to complete and shutdown executor
private static void someTask( ... ) {
ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// executor runs a bunch of tasks in parallel here and then shutdown executor
}
My question is if i need to adjust number of thread pool since 2 tasks running in parallel try to create thread pool of the size of # of available cores? Will this work or will this block one of the initial task until we release the threads by shutting down?
===== Initial question =====
My question is slightly different that this question.
I have 2 tasks, someTask1 and someTask2 which i am planning to parallelize. The problem is (i am not sure if this could be a problem or not) that someTask1 uses ForkJoinPool and runs a bunch of other tasks in parallel. Can I parallelize the 2 tasks someTask1 and someTask2 using ForkPoolJoin something like the code below even though one of the task i am parallelizing runs other tasks in parallel? If its possible can i get some help on refactoring the code below:
ForkPoolJoin forkJoinPool = new ForkJoinPool(parallelism);
if (someCond1) {
forkJoinPool.submit(() -> someTask1( ... ));
}
if (someCond2) {
forkJoinPool.submit(() -> someTask2( ... ));
}
// I want to wait here until forkJoinPool is done with both tasks (or either of the 1 based on each condition)?
private void someTask1( ... ) {
ForkPoolJoin forkJoinPool = new ForkJoinPool(parallelism);
try {
forkJoinPool.submit(() -> someStream.stream().map( ... ))
.get(); // Wait for all the tasks to complete
} finally {
forkJoinPool.shutdown();
}
}
Edit:
I figured out a way to deal with the waiting issue. My solution is since i only have 2 tasks in parallel, i store the result of the forkJoinPool.submit into 2 Future variables and call on their get() function one by one. If someone else has a better more generic solution, that would be great.

The use of executor.shutdown()

Look at the following piece of code:
public void pinger()
{
ScheduledThreadPoolExecutor executor = new ScheduledThreadPoolExecutor(1);
executor.scheduleAtFixedRate(runnable, start, rate, TimeUnit.SECONDS);
executor.shutdown();
}
Is there any use of writing the shutdown command in this case? Different clients would create their own runnable objects and invoke this function.
When you shutdown an executor, no new task will be accepted. Since you create a new one inside the pinger method every task has its own executor. A shutdown as you write will only free resource once the corrent task is terminated.
Some notes:
You should not create Executor for each client request.
Create Executor out side of client request and submit tasks to Executor
When you decide that you should not accept new tasks to Executor, then shutdown the executor. The right way of shutting down Executor is explained in below post:
How to forcefully shutdown java ExecutorService

What's the difference between canceling a periodic task via the future versus shutting down the executor service the task came from?

Difference between:
A)
ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
ScheduledFuture<> periodicTask = executorService.scheduleAtFixedRate(() -> {
try {
doSomething();
} catch (Exception e) {
log.error("Unhandled exception caught whilst doing minutely run", e);
}
}, initialDelay, PERIOD, TimeUnit.MILLISECONDS);
// In a shutdown hook:
periodicTask.cancel(true);
Does that cancel all the running tasks? Does it kill the ExecutorService?
B) The other way would be:
executorService.shutdown();
executorService.awaitTermination(....);
What's the difference?
Also how do I know how many tasks in the future the executorservice schedules?
Once I get the shut down signal I just want to run my scheduled task around 2 - 3 more times until I reach a certain condition. After that I want to kill it.
periodicTask.cancel(true);
The cancel method only stops the unstarted jobs and interrupts the running thread which then must return from the run() method.
executorService.shutdown();
The shutdown() method prevents clients sending more work to the executor service. This means all the existing tasks will run to completion.
executorService.awaitTermination(....);
This helps the application shut down gracefully. i.e. The executor service takes no more work and waits till all the executing jobs finish and then shuts-down.

Schedule multiple tasks on multiple threads

I need to perform some data collection periodically, for that I want to create a task which requests some data from different servers. Some servers will take more time to process the request and return the response than others.
That's why I want to create a task for each server and execute the tasks async. If i'm using ScheduledExecutorService in the following way will each task execute in its own thread or all the tasks will be executed in the same thread?
What happens if a task is throwing an exception all the other scheduled tasks will fail?
this.scheduler = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
#Override
public Thread newThread(Runnable r) {
return new Thread(r, "collectionThread");
}
});
for (String url:urls){
this.scheduler.scheduleWithFixedDelay(new CollectorTask(url),
startupDelaySeconds,
scheduleRateSeconds,
TimeUnit.SECONDS);
}
You're using a single-threaded executor service, so all tasks are being executed sequentially. If any of them throws an exception, all next tasks executions are cancelled.
You could use
Executors.newScheduledThreadPool(4) // 4 is the max number of parallel jobs
... to allow parallel execution and wrap a body of a submitted job into
try {
...
} catch(Exception e){
logger.warn("exception during task execution", e);
}
to log errors without propagating them.
Minor delays (several milliseconds) are possible and depend on the OS, tasks will never execute earlier than their scheduled time. Task's execution can be delayed due to previous long runs or lack of free threads, but the following executions will be run by the original schedule: initialDelay + n * period.
Yes, what you do is create two thread executors. The first is a scheduled executor which takes a runnable that is meant to start your actual runnable. All this runnable does it create an instance of your task runnable and submit it to the real executor. That executor should just be a normal thread pool that will handle the tasks.
private final ScheduledExecutorService scheduledExecutor = Executors
.newSingleThreadScheduledExecutor();
private final ExecutorService executor = Executors.newCachedThreadPool();
private class SubmitTaskRunnable implements Runnable {
#Override
public void run() {
executor.execute(new TaskRunnable());
}
}
Schedule the SubmitTaskRunnable on your scheduled executor because that one will not throw any exceptions. Let your actual task run inside a cached executor will allow multiple tasks to run concurrently even if the previous ones have not finished.

How to stop immediately a task which is started using an ExecutorService?

I have tried many different ways to immediately stop a task which is started using an ExecutorService, with no luck.
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call () {
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
... do many other things here..
if(Thread.currentThread.isInterrupted()) {
return null;
}
}
));
if(flag) { // may be true and directly cancel the task
future.cancel(true);
}
Sometimes I need to cancel the task immediately after it is started, you may be curious why I want to do this, well you may imagine a senario that a user accidentally hits the "Download" button to start a "Download Task" and he immediately wants to cancel the action because it was just an accidental click.
The problem is that after calling future.cancel(true), the task is not stopped and Thread.currentThread.isInterrupted() still returns false and I have no way to know the task was stopped from inside the call() method.
I am thinking of setting a flag like cancelled=true after calling future.cancel(true) and checking that flag constantly in the call() method, I think this is a hack and the code could be very ugly because the user can start many tasks at the same moment.
Is there a more elegant way of achieving what I want?
EDIT:
This really drives me mad. I have spent almost a day on this problem now. I will try to explain a little bit more for the problem I am facing.
I do the following to start 5 tasks, each task will start 5 threads to download a file. and then I stop all 5 tasks immediately. For all of the method calls below, i start a thread(ExecutorService.submit(task)) to make it asynchronous as you can tell from the suffixes of the methods.
int t1 = startTaskAysnc(task1);
int t2 = startTaskAysnc(task2);
int t3 = startTaskAysnc(task3);
int t4 = startTaskAysnc(task4);
int t5 = startTaskAysnc(task5);
int stopTaskAysnc(t1);
int stopTaskAysnc(t2);
int stopTaskAysnc(t3);
int stopTaskAysnc(t4);
int stopTaskAysnc(t5);
in startTaskAysnc(), I simply initiate a socket connection to remote server to get the size of the file(and this certainly is gonna take some time), after successfully getting the fileSize, I will start 5 threads to download different parts of the file. like the following(the code is simplified to make it more easy to follow):
public void startTaskAsync(DownloadTask task) {
Future<Void> future = executorService.submit(new Callable<Void>(
public Void call () {
// this is a synchronous call
int fileSize = getFileSize();
System.out.println(Thread.currentThread.isInterrupted());
....
Future<Void> futures = new Future<Void>[5];
for (int i = 0; i < futures.length; ++i) {
futures[i] = executorService.submit(new Callable<Void>(){...});
}
for (int i = 0; i < futures.length; ++i) {
futures[i].get(); // wait for it to complete
}
}
));
synchronized (mTaskMap) {
mTaskMap.put(task.getId(), future);
}
}
public void stopTaskAysnc(int taskId) {
executorService.execute(new Runnable(){
Future<Void> future = mTaskMap.get(taskId);
future.cancel(true);
});
}
I noticed a weird behavior that after I called stopTaskAsync() for all 5 tasks, there would always be at least one task that got stopped(i.e. Thread.currentThread.isInterrupted() return true), and the other 4 tasks kept running.
And I have tried your suggestions by setting an UncaughtExceptionHandler, but nothing comes out from that.
EDIT:
The problem was solved in this link: Can't stop a task which is started using ExecutorService
Well, the javadoc of Future.cancel(boolean) says that:
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.
so it's quite certain that the thread that executes the task is interrupted. What could have happened is that one of the
... do many other things here..
is accidentally clearing the Thread's interrupted status without performing the desired
handling. If you'll put a breakpoint in Thread.interrupt() you might catch the criminal.
Another option I can think of is that the task terminates before capturing the interrupt, either because it's completed or thrown some uncaught exception. Call Future.get() to determine that. Anyway, as asdasd mentioned, it is a good practice to set an UncaughtExceptionHandler.
What you're doing is very dangerous: you're using a thread pool to execute tasks (which I'll call downloaders), and the same thread pool to execute tasks which
wait for the downloaders to finish (which I'll call controllers)
or ask the controllers to stop
This means that if the core number of threads is reached after the controller has started, the downloaders will be put in the queue of the thread pool, and the controller thread will never finish. Similarly, if the core number of threads is reached when you execute the cancelling task, this cancelling task will be put in the queue, and won't execute until some other task is finished.
You should probably use a thread pool for downloaders, another one for controllers, and the current thread to cancel the controllers.
I think you'll find solution here. The main point is that cancel method raises InterruptedException. Please check if your thread is still running after cancellation? Are you sure that you didn't try to interrupt finished thread? Are you sure that your thread didn't fail with any other Exception? Try to set up UncaughtExceptionHandler.

Categories