I have request and multiple threads, which are looking for result in different ways and each thread should get some result at some point.. I need to take the result from the first finished thread, return this result and kill all of the remaining threads. I also have timeout when I'll return some default result..
I can think of two solutions, none of which seems "correct" to me..
1) Loop through the tasks, ask if they are finished, sleep for a while and return the first finished task it founds..
import java.util.ArrayList;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import org.apache.log4j.LogManager;
public class App {
public static final ExecutorService executors = Executors.newCachedThreadPool();
public static void main(String[] args) {
ArrayList<Future<String>> taskList = new ArrayList<>();
taskList.add(executors.submit(new Task1()));
taskList.add(executors.submit(new Task2()));
String result = null;
long start = System.currentTimeMillis();
long timeout = 1000;
while ((start + timeout) < System.currentTimeMillis()) {
try {
for (Future<String> future : taskList) {
if (future.isDone()) {
result = future.get();
break;
}
}
if (result != null) {
break;
}
Thread.sleep(10);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
if (result == null) {
result = "default..";
}
}
}
class Task1 implements Callable<String> {
@Override
public String call() throws Exception {
// find result in one way
return new String("result..");
}
}
class Task2 implements Callable<String> {
@Override
public String call() throws Exception {
// find result in some other way
return new String("result..");
}
}
2) Monitor each task with another thread by calling future.get(timeout, TimeUnit.MILLISECONDS);
and the first finished thread would then call future.cancel(true);
for all other threads...
The first solution seems to me like wasting processor time, and the second seems to me like wasting threads..
Finally, the Question is: Is there any better solution?
Thank you in advance
Edit: Thank you all for answers, I have solved this using "John H" 's answer:
There is a built in function that does this: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#invokeAny%28java.util.Collection,%20long,%20java.util.concurrent.TimeUnit%29
This will invoke all the tasks you give it, and wait for the first one to return an answer up to a time limit. If none of them return a result in time you can catch the TimeoutException and return the default value. Otherwise you can use the first value it returns and it will take care of cancelling the rest of the tasks.
There is a built in function that does this: https://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ExecutorService.html#invokeAny%28java.util.Collection,%20long,%20java.util.concurrent.TimeUnit%29
This will invoke all the tasks you give it, and wait for the first one to return an answer up to a time limit. If none of them return a result in time you can catch the TimeoutException and return the default value. Otherwise you can use the first value it returns and it will take care of cancelling the rest of the tasks.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With