ExecutorService have invokeAll method, and documentation say next:
Executes the given tasks, returning a list of Futures holding their
status and results when all complete or the timeout expires, whichever
happens first. Future.isDone is true for each element of the returned
list. Upon return, tasks that have not completed are cancelled. Note
that a completed task could have terminated either normally or by
throwing an exception. The results of this method are undefined if the
given collection is modified while this operation is in progress
Consider the following code:
public class Main {
public static void main(String[] args) {
ExecutorService executorService = Executors.newFixedThreadPool(3);
List<Callable<Integer>> tasks = Arrays.asList(
Main::veryLongCalculations,
Main::veryLongCalculations,
Main::veryLongCalculations);
try {
List<Future<Integer>> resultTasks = executorService.invokeAll(tasks, 2, TimeUnit.SECONDS);
for (Future<Integer> task: resultTasks) {
System.out.println("is done: " + task.isDone() + ", canceled: " + task.isCancelled());
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static Integer veryLongCalculations() {
while (!Thread.currentThread().isInterrupted()) {
//very long calculations
}
System.out.println("interrupted");
return 0;
}
}
If we run this code, the following output will be displayed on the screen:
interrupted
interrupted
interrupted
is done: true, canceled: true
is done: true, canceled: true
is done: true, canceled: true
The tasks were clearly running longer than the timeout and so they were cancelled. Status of tasks is completed. Everything worked exactly as expected.
But if we use ForkJoinPool as ExecutorService (ExecutorService executorService = Executors.newWorkStealingPool()) then the output will be as like this:
interrupted
interrupted
is done: true, canceled: true
is done: true, canceled: true
is done: false, canceled: false
One of the tasks is never cancelled, which contradicts the documentation
But if we change sdk from java17 to java11, then it will work fine.
I began to understand and saw that ForkJoinPool inherits from the AbstractExecutorService class, which implements the invokeAll method. But in java 17 this method is overloaded in the ForkJoinPool itself. Here is the overloaded method:
#Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException {
long nanos = unit.toNanos(timeout);
ArrayList<Future<T>> futures = new ArrayList<>(tasks.size());
try {
for (Callable<T> t : tasks) {
ForkJoinTask<T> f =
new ForkJoinTask.AdaptedInterruptibleCallable<T>(t);
futures.add(f);
externalSubmit(f);
}
long startTime = System.nanoTime(), ns = nanos;
boolean timedOut = (ns < 0L);
for (int i = futures.size() - 1; i >= 0; --i) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
if (timedOut)
ForkJoinTask.cancelIgnoringExceptions(f);
else {
((ForkJoinTask<T>)f).awaitPoolInvoke(this, ns);
if ((ns = nanos - (System.nanoTime() - startTime)) < 0L)
timedOut = true;
}
}
}
return futures;
} catch (Throwable t) {
for (Future<T> e : futures)
ForkJoinTask.cancelIgnoringExceptions(e);
throw t;
}
}
On the first iteration, the timedOut variable will always be false, so one task will never be cancelled. Is this a bug? Or is there some other reason why it works this way? This behavior is clearly contrary to the documentation, which says that all tasks should have a completed status and should be canceled if the timeout expires.
Related
This is a simplified example I did to expose my problem. I have some task doSomeWork() that I handle in a multihreading fashion using ExecutorService (4 threads at a time max). However, if any of the threads/tasks generates an exception, I would like to:
Stop any further tasks from being processed.
Catch the exception at the main thread level.
public static void main(String[] args) {
final ExecutorService threadPool = Executors.newFixedThreadPool(4);
final ExecutorCompletionService<Void> completionService = new ExecutorCompletionService<>(threadPool);
try {
for (int i = 0; i < 10; i++) {
int b = i;
completionService.submit(() -> doSomeWork(b));
}
threadPool.shutdown();
threadPool.awaitTermination(8, TimeUnit.HOURS);
System.exit(0);
} catch (Exception e) {
System.out.println("Something wrong happened: " + e.getMessage());
}
System.exit(1);
}
//This function have 50% odds of throwing an exception
public static Void doSomeWork(int i) throws Exception {
Thread.sleep(500);
if ((Math.random() > 0.5))
{
System.out.println("I have reached indice: " + i);
}
else
{
throw new Exception("I couldn't handle indice " + i);
}
return null;
}
Currently, an execution would output something like this:
I have reached indice: 0
I have reached indice: 2
I have reached indice: 1
I have reached indice: 4
I have reached indice: 6
I have reached indice: 7
I have reached indice: 5
I have reached indice: 9
As you can see, indice 3 is missing, nevertheless the execution of the remaining threads completed. It also didn't output anything about the exception.
My desired output would be something like this:
I have reached indice: 0
I have reached indice: 2
I have reached indice: 1
Something wrong happened: I couldn't handle indice 3
Other solutions I found around this problem were using a callable with a future but in a blocking fashion. I can't block the execution of other threads while waiting for the future otherwise this whole multithreading is pointless.
You can do that using CompletableFuture. This is your main function I tested:
final ExecutorService executorService = Executors.newFixedThreadPool(4);
final List<CompletableFuture<Void>> all = new ArrayList<>();
try {
for (int i = 0; i < 10; i++) {
int b = i;
CompletableFuture<Void> v = CompletableFuture.runAsync(() -> {
try {
doSomeWork(b);
} catch (Exception e) {
throw new RuntimeException(e);
}
},
executorService);
all.add(v);
}
CompletableFuture<Void> placeholder = CompletableFuture.allOf(all.toArray(new CompletableFuture[0]));
failFast(all, placeholder);
System.out.println("All tasks ended");
} catch (Exception e) {
System.out.println("Something wrong happened: " + e.getMessage());
} finally {
executorService.shutdownNow();
}
Utility function to make the joint future fail as soon as one of them is failed (or when all of them are completed):
private static <T> void failFast(List<CompletableFuture<T>> futures, CompletableFuture<T> joint) {
while (true) {
if (joint.isDone()) {
return;
}
for (CompletableFuture<T> future : futures) {
if (future.isCompletedExceptionally()) {
return;
}
}
}
}
And this is the output I get:
I have reached indice: 1
I have reached indice: 7
I have reached indice: 5
I have reached indice: 4
Something wrong happened: java.lang.RuntimeException: java.lang.Exception: I couldn't handle indice 0
Explanation:
The method CompletableFuture.runAsync() allows you to provide with a Runnable (your doSomeWork) and an executor with a certain number of threads. Here, I pass an executor with 4 threads (as you did in your example).
Inside the runnable, I don't only run the doSomeWork function but I also catch Exception and throw a RuntimeException (need to do that because Lambdas do not support checked exceptions, so I need to wrap it into a runtime one but it will still interrupt execution and be catched by your main).
Each time I create a new CompletableFuture<Void> for the task with the given index i, I will store this result into a list of completable futures.
The for loop will take nothing to execute, since the completable futures run asynchronously.
Hence, I create a joint completable future with CompletableFuture.allOf(...) and then I use the utility function failFast on this future in order to stop as soon as one of the task is failed (or to continue until all of them are complete).
So basically as soon as one of the futures fails throwing an exception, the joint future is considered to be completed and will hence leave the handle to your main thread which is, meanwhile, being hit by the RuntimeException that was thrown inside the lambda expression.
Note: thanks to Thomas' comment, I've updated the code to use an ExecutorService instead of a simple Executor. That allows you to have a call to shutdownNow() inside your finally block after you catch the exception.
As Thomas suggests, also, you may directly throw a RuntimeException inside your doSomeWork function so you don't need to catch and wrap inside the lambda expression.
Other note: #matt made me notice something I didn't know. The .allOf() future will be completed when ALL futures are completed, whether successfully or not.
Hence, as he pointed out, my solution wouldn't work as is. I've edited again the answer to take his comment into account, thanks #matt for making me notice.
It sounds like you've excluded a correct way to do this, based on an incorrect assumption. Keep your futures.
List<Future<?>> futures = new ArrayList<>();
Then when you submit.
futures.add( completionService.submit( () -> doSomeWork(b) ) );
Now, you can check the futures status in your main thread.
for(Future<?> f: futures){
try{
f.get();
} catch( ExecutionException e){
//execution exception handled on the main thread.
completionService.shutdownNow();
} catch( InterruptedException ie){
//what should happen here.
}
}
That way, shutdownNow gets called, so all of the non-started tasks get returned and will not start.
You could use a timeout with get to check every task since some will be running in parallel.
Here is a complete compilable example.
import java.util.concurrent.*;
import java.util.*;
public class ExecutorJunk{
static int count = 0;
static void task(){
int z = count++;
if(z == 3){
throw new RuntimeException("z is 3");
}
System.out.println("z: " + z);
try{ Thread.sleep(1500);} catch(InterruptedException e){};
}
public static void main(String[] args){
ExecutorService service = Executors.newFixedThreadPool(4);
List<Future<?>> all = new ArrayList<>();
for(int i = 0; i<10; i++){
all.add( service.submit(ExecutorJunk::task) );
}
service.shutdown();
try{
while(!service.isTerminated()){
for(Future f: all){
try{
f.get(1, TimeUnit.MILLISECONDS);
} catch( TimeoutException toe){
//pass.
}
}
}
} catch(Exception e){
System.out.println( service.shutdownNow().size() + " tasks not started");
e.printStackTrace();
}
}
}
When I run this I get.
z: 0
z: 1
z: 2
z: 4
5 tasks not started
java.util.concurrent.ExecutionException: java.lang.RuntimeException: z is 3
...
It probably could be done a bit smarter. Such as purging the list of futures as get succeeds, instead of using a timeout just check the futures if they're done, then doing a future.get.
I am using Java 8, and I want to know the recommended way to enforce timeout on 3 async jobs that I would to execute async and retrieve the result from the future. Note that the timeout is the same for all 3 jobs. I also want to cancel the job if it goes beyond time limit.
I am thinking something like this:
// Submit jobs async
List<CompletableFuture<String>> futures = submitJobs(); // Uses CompletableFuture.supplyAsync
List<CompletableFuture<Void>> all = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
try {
allFutures.get(100L, TimeUnit.MILLISECONDS);
} catch (TimeoutException e){
for(CompletableFuture f : future) {
if(!f.isDone()) {
/*
From Java Doc:
#param mayInterruptIfRunning this value has no effect in this
* implementation because interrupts are not used to control
* processing.
*/
f.cancel(true);
}
}
}
List<String> output = new ArrayList<>();
for(CompeletableFuture fu : futures) {
if(!fu.isCancelled()) { // Is this needed?
output.add(fu.join());
}
}
return output;
Will something like this work? Is there a better way?
How to cancel the future properly? Java doc says, thread cannot be interrupted? So, if I were to cancel a future, and call join(), will I get the result immediately since the thread will not be interrupted?
Is it recommended to use join() or get() to get the result after waiting is over?
It is worth noting that calling cancel on CompletableFuture is effectively the same as calling completeExceptionally on the current stage. The cancellation will not impact prior stages. With that said:
In principle, something like this will work assuming upstream cancellation is not necessary (from a pseudocode perspective, the above has syntax errors).
CompletableFuture cancellation will not interrupt the current thread. Cancellation will cause all downstream stages to be triggered immediately with a CancellationException (will short circuit the execution flow).
'join' and 'get' are effectively the same in the case where the caller is willing to wait indefinitely. Join handles wrapping the checked Exceptions for you. If the caller wants to timeout, get will be needed.
Including a segment to illustrate the behavior on cancellation. Note how downstream processes will not be started, but upstream processes continue even after cancellation.
public static void main(String[] args) throws Exception
{
int maxSleepTime = 1000;
Random random = new Random();
AtomicInteger value = new AtomicInteger();
List<String> calculatedValues = new ArrayList<>();
Supplier<String> process = () -> { try { Thread.sleep(random.nextInt(maxSleepTime)); System.out.println("Stage 1 Running!"); } catch (InterruptedException e) { e.printStackTrace(); } return Integer.toString(value.getAndIncrement()); };
List<CompletableFuture<String>> stage1 = IntStream.range(0, 10).mapToObj(val -> CompletableFuture.supplyAsync(process)).collect(Collectors.toList());
List<CompletableFuture<String>> stage2 = stage1.stream().map(Test::appendNumber).collect(Collectors.toList());
List<CompletableFuture<String>> stage3 = stage2.stream().map(Test::printIfCancelled).collect(Collectors.toList());
CompletableFuture<Void> awaitAll = CompletableFuture.allOf(stage2.toArray(new CompletableFuture[0]));
try
{
/*Wait 1/2 the time, some should be complete. Some not complete -> TimeoutException*/
awaitAll.get(maxSleepTime / 2, TimeUnit.MILLISECONDS);
}
catch(TimeoutException ex)
{
for(CompletableFuture<String> toCancel : stage2)
{
boolean irrelevantValue = false;
if(!toCancel.isDone())
toCancel.cancel(irrelevantValue);
else
calculatedValues.add(toCancel.join());
}
}
System.out.println("All futures Cancelled! But some Stage 1's may still continue printing anyways.");
System.out.println("Values returned as of cancellation: " + calculatedValues);
Thread.sleep(maxSleepTime);
}
private static CompletableFuture<String> appendNumber(CompletableFuture<String> baseFuture)
{
return baseFuture.thenApply(val -> { System.out.println("Stage 2 Running"); return "#" + val; });
}
private static CompletableFuture<String> printIfCancelled(CompletableFuture<String> baseFuture)
{
return baseFuture.thenApply(val -> { System.out.println("Stage 3 Running!"); return val; }).exceptionally(ex -> { System.out.println("Stage 3 Cancelled!"); return ex.getMessage(); });
}
If it is necessary to cancel the upstream process (ex: cancel some network call), custom handling will be needed.
After calling cancel you cannot join the furure, since you get an exception.
One way to terminate the computation is to let it have a reference to the future and check it periodically: if it was cancelled abort the computation from inside.
This can be done if the computaion is a loop where at each iteration you can do the check.
Do you need it to be a CompletableFuture? Cause another way is to avoid to use a CompleatableFuture, and use a simple Future or a FutureTask instead: if you execute it with an Executor calling future.cancel(true) will terminate the computation if possbile.
Answerring to the question: "call join(), will I get the result immediately".
No you will not get it immediately, it will hang and wait to complete the computation: there is no way to force a computation that takes a long time to complete in a shorter time.
You can call future.complete(value) providing a value to be used as default result by other threads that have a reference to that future.
I have a set of elements and for each of them I'm executing method, by passing it to CompletableFuture.runAsync() as Runnable. During execution, there may be a need to stop whole calculations, so I check some condition before execution of method. If calculations should be stopped, then I throw an exception, which is handled outside of CompletableFuture. I would like to prevent execution of all Runnables, which are executed after exception was thrown. So, in other words, I don't want to wait for all CompletableFutures to complete, when any of them throws an exception.
Set elements = ...
Executor executor = Executors.newFixedThreadPool(N);
try {
CompletableFuture.allOf(elements.stream().map(e - > CompletableFuture.runAsync(() - > {
if (shouldStop()) {
throw new MyException();
}
myMethod(e);
}, executor)).toArray(CompletableFuture[]::new)).join()
} catch (CompletionException e) {
...
}
Just cancel all of them when an exception occurs. The obstacles are that you are not knowing all of them when creating them and that you don’t want to do this work more than once. This can be solved by creating a new, empty CompletableFuture first (let’s call it f1). Then, create the futures as before, but insert a call to f1.cancel in the if(shouldStop()) { … } statement. Then, after all futures have been created, chain an action that will cancel all of them to the f1 future.
The cancellation will serve both purposes, It will prevent the execution of runnables which have not started yet and it will make the future returned by allOf not wait for the completion of still ongoing evaluations.
Since cancelling a CompletableFuture is not different to completing it exceptionally with a CancellationException and in case of multiple exceptions, the future returned by allOf will report an arbitrary one, we may use completeExceptionally with the custom MyException instead, to ensure that the reported exception will not be a secondary CancellationException.
A self contained example would be:
static final AtomicInteger STOP = new AtomicInteger(2);
static boolean shouldStop() {
return STOP.getAndDecrement() <= 0;
}
static final int N = 10;
public static void main(String[] args) {
Set<Integer> elements = IntStream.range(0, 100).boxed().collect(Collectors.toSet());
ExecutorService executor = Executors.newFixedThreadPool(N);
try {
CompletableFuture<?> cancelAll = new CompletableFuture<>();
CompletableFuture<?>[] all = elements.stream()
.map(e ->
CompletableFuture.runAsync(() -> {
System.out.println("entered "+e);
if(shouldStop()) {
RuntimeException myException = new RuntimeException("stopped");
// alternatively cancelAll.cancel(false);
cancelAll.completeExceptionally(myException);
throw myException;
}
System.out.println("processing "+e);
}, executor))
.toArray(CompletableFuture<?>[]::new);
cancelAll.whenComplete((value,throwable) -> {
if(throwable != null) {
for(CompletableFuture<?> cf: all) cf.completeExceptionally(throwable);
}
});
CompletableFuture.allOf(all).join();
} catch (CompletionException e) {
e.printStackTrace();
}
executor.shutdown();
}
which will print something like
entered 3
entered 8
entered 4
entered 6
entered 1
entered 9
entered 0
entered 7
entered 5
entered 2
entered 10
processing 8
processing 3
java.util.concurrent.CompletionException: java.lang.RuntimeException: stopped
at java.base/java.util.concurrent.CompletableFuture.encodeThrowable(CompletableFuture.java:331)
at java.base/java.util.concurrent.CompletableFuture.completeThrowable(CompletableFuture.java:346)
at java.base/java.util.concurrent.CompletableFuture$BiRelay.tryFire(CompletableFuture.java:1423)
at java.base/java.util.concurrent.CompletableFuture$CoCompletion.tryFire(CompletableFuture.java:1144)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
at CompletableFutureTest.lambda$main$3(CompletableFutureTest.java:34)
at java.base/java.util.concurrent.CompletableFuture.uniWhenComplete(CompletableFuture.java:859)
at java.base/java.util.concurrent.CompletableFuture$UniWhenComplete.tryFire(CompletableFuture.java:837)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:506)
at java.base/java.util.concurrent.CompletableFuture.completeExceptionally(CompletableFuture.java:2088)
at CompletableFutureTest.lambda$main$0(CompletableFutureTest.java:26)
at java.base/java.util.concurrent.CompletableFuture$AsyncRun.run(CompletableFuture.java:1736)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base/java.lang.Thread.run(Thread.java:834)
Caused by: java.lang.RuntimeException: stopped
at CompletableFutureTest.lambda$main$0(CompletableFutureTest.java:25)
... 4 more
showing that due to the concurrency, some of the runnables are already running but no subsequent execution will be started once the cancellation has been propagated.
Note that since the cancelAll will only be completed exceptionally or never at all, you could simplify the chained action to cancelAll.whenComplete((value,throwable) -> { for(CompletableFuture<?> cf: all) cf.completeExceptionally(throwable); }); but it’s just a matter of coding style whether to keep the redundant check or not.
You may also add a delay to the processing step to see that allOf(all).join() won’t wait for completion if the stop condition has been fulfilled.
It’s also possible to chain an action to the futures returned by runAsync which will cancel all of them on any exceptional completion, not just the explicit stopping. But then, care must be taken to return the original future representing the operation scheduled via runAsync instead of the future returned by whenComplete.
CompletableFuture<?> cancelAll = new CompletableFuture<>();
CompletableFuture<?>[] all = elements.stream()
.map(e -> {
CompletableFuture<Void> cf = CompletableFuture.runAsync(() -> {
System.out.println("entered "+e);
if(shouldStop()) throw new RuntimeException("stopped");
System.out.println("processing "+e);
}, executor);
cf.whenComplete((value,throwable) -> {
if(throwable != null) cancelAll.completeExceptionally(throwable);
});
return cf;
})
.toArray(CompletableFuture<?>[]::new);
cancelAll.whenComplete((value,throwable) -> {
for(CompletableFuture<?> cf: all) cf.completeExceptionally(throwable);
});
CompletableFuture.allOf(all).join();
I don't have much (well any!) experience with CompletableFutures but I do have a suggestion (may be of help?)
Can you declare the lambda within the CompletableFuture.allOf(elements.stream().map outside the try block. This way non of the futures get run, until inside the try. but they are still accessible to the catch block. Within that you can then cancel all of them.
The main thing you should do is interrupt all the running tasks you want to terminate faster, which means that these tasks may need to check for interrupts so they can know to stop what they were doing and terminate faster.
Additionally, rather than waiting for the interrupted tasks to actually terminate you can continue in the main thread and let them terminate in the background.
public static void main(String[] args) {
List<Integer> elements = Arrays.asList(5, null, 6, 3, 4); // these elements will fail fast
// List<Integer> elements = Arrays.asList(5, 2, 6, 3, 4); // these elements will succeed
try {
CountDownLatch latch = new CountDownLatch(elements.size());
ExecutorService executor = Executors.newFixedThreadPool(elements.size());
elements.stream().forEach(e -> {
executor.execute(() -> {
try {
doSomething(e);
latch.countDown();
} catch (Exception ex) {
// shutdown executor ASAP on exception, read the docs for `shutdownNow()`
// it will interrupt all tasks in the executor
if (!executor.isShutdown()) {
executor.shutdownNow();
}
for (int i = (int) latch.getCount(); i >= 0; i--) {
latch.countDown();
}
// log the exception
ex.printStackTrace(System.out);
}
});
});
latch.await();
if (executor.isShutdown()) {
System.out.println("Tasks failed! Terminating remaining tasks in the background.");
} else {
executor.shutdown();
System.out.println("Tasks succeeded!");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void doSomething(Integer sleepSecs) {
// You will want to check for `interrupted()` throughout the method you want to be able to cancel
if (Thread.interrupted()) {
System.out.println(Thread.currentThread().getName() + " interrupted early");
return;
}
if (sleepSecs == null) {
System.out.println(Thread.currentThread().getName() + " throwing exception ");
throw new RuntimeException();
}
try {
System.out.println(Thread.currentThread().getName() + " started interruptable sleep for " + sleepSecs + "s");
Thread.sleep(sleepSecs * 1000);
System.out.println(Thread.currentThread().getName() + " finished interruptable sleep" + sleepSecs + "s");
} catch (InterruptedException e) {
System.out.println(Thread.currentThread().getName() + " interrupted sleep!");
}
// ...possibly some part of the task that can't be skipped, such as cleanup
System.out.println(Thread.currentThread().getName() + " complete!");
}
public class FutureGetTimeoutTest {
private static final ExecutorService THREAD_POOL = Executors.newFixedThreadPool(5);
public static void main(String[] args) throws InterruptedException, ExecutionException {
List<String> respList = new ArrayList<String>();
List<Future<String>> futures = new ArrayList<Future<String>>();
futures.add(THREAD_POOL.submit(new CallableTask(1L)));
futures.add(THREAD_POOL.submit(new CallableTask(2L)));
futures.add(THREAD_POOL.submit(new CallableTask(3L)));
long start = System.currentTimeMillis();
System.out.println(start);
for (Future<String> future : futures) {
try {
respList.add(future.get(10000, TimeUnit.MILLISECONDS));
/*
* Timeout time for 2nd Task starts only at the end of 1st Task Timeout
* and so 2nd task is able to run for 20s and 3rd task for 30s!
*/
} catch (TimeoutException e) {
e.printStackTrace();
}
}
long end = System.currentTimeMillis();
System.out.println(end);
System.out.println(end - start);
System.out.println(respList);
}
}
class CallableTask implements Callable<String> {
private long ipAddressL;
public CallableTask(long ipAddressL) {
this.ipAddressL = ipAddressL;
}
#Override
public String call() throws Exception {
if (ipAddressL == 1) {
Thread.sleep(10000);
/* Imagine a DB operation taking more time. */
return "1";
} else if (ipAddressL == 2) {
Thread.sleep(20000);
return "2";
} else {
Thread.sleep(30000);
return "3";
}
}
}
I want to return a empty string or just terminate the thread or throw a TimeoutException from within each task if each task takes more than 10 seconds individually.
Say 1st thread takes 10s, Future.get() will wait for 10s and then timeout. I will catch the exception and proceed the iteration for 2nd future object. Say 2nd thread is not completed by this time (which means it ran for 10s while 1st thread ran and is still running), now Future.get() on 2nd thread will wait for another 10s and so a total of 20s and so on for subsequent threads.
future.get(1000, TimeUnit.MILLISECONDS) (1 sec),
will ensure 10s limit for the whole operation but I need a 10s limit on the whole operation by having a 10s limit on each individual concurrent task.
Use THREAD_POOL.invokeAll instead of submit to wait 10s for the tasks to complete.
If some of the tasks have completed before 10 seconds pass, you can check for that with future.isDone() and retrieve the result without blocking using future.get.
So I have some Callable tasks, sensitive to interruptions, which I submit to the ExecutorService using invokeAll. After 5 seconds from another method I call executorService.shutdownNow after which I call the awaitTermination, which returns true, so all seems good. The problem is the executor never terminates.
Due to logging I know that each one of my tasks finished.
nevertheless the invokeAll still blocks on f.get when i is equal to the number of threads of the executor:
The following code is obtained from AbstractExecutorService + some logging.
#Override
public <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException {
if (tasks == null) throw new NullPointerException();
ArrayList<Future<T>> futures = new ArrayList<Future<T>>(tasks.size());
boolean done = false;
try {
List<Callable<T>> list = new ArrayList<Callable<T>>();
for (Callable<T> t : tasks) {
list.add(t);
RunnableFuture<T> f = newTaskFor(t);
futures.add(f);
execute(f);
}
for (int i = 0, size = futures.size(); i < size; i++) {
Future<T> f = futures.get(i);
if (!f.isDone()) {
log.info("Future %s is not done!. Task %s", i, list.get(i));
try {
log.info("Get from future %s", i);
// NEXT LINE BLOCKS FOR i= NUMBER OF THREADS
f.get();
log.info("Got result from future %s", i);
} catch (CancellationException ignore) {
} catch (ExecutionException ignore) {
}
}
}
log.info("Obtained all!");
done = true;
return futures;
} finally {
if (!done) for (int i = 0, size = futures.size(); i < size; i++)
futures.get(i).cancel(true);
}
}
Am I not suppose to use invokeAll with shutdown? I guess not, after all they are in the same class. Why does it get blocked, only when i= the number of threads of the executor?
Yes, you're not suppose to use invokeAll with shutdown. At least this is what I understand, correct me if I'm wrong.
The shutdownNow method:
public List<Runnable> shutdownNow() {
...
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
tasks = drainQueue();
...
}
The only thing is does is interrupt working threads and remove the rest of the runnables from the working queue, see drainQueue. ShutdownNow/Shutdown does not modify the futures in our invokeAll method
So what happens in my case is that for an Executor with N threads, I invoke 300 jobs, each of them take more than 1 minute, after 5 seconds I cancel (interrupt working threads), N threads are interrupted (0 to N-1). What happens with the rest of the futures? Nothing, the next call to f.get() (see corresponding line in the question) will block and you're stuck there. This explains why I'm always blocked on i = Number of threads.