Home > OS >  Java ExecutorService (Thread Pool): Wait for the first task (Future) to finish and return immediatel
Java ExecutorService (Thread Pool): Wait for the first task (Future) to finish and return immediatel

Time:10-04

I need to call a few (less than 30) REST APIs concurrently, and immediately return the first response that I get from those API calls.

I implemented a method like this:

public String fetchFastest(List<URL> urls) throws ExecutionException, InterruptedException {
    var executor = Executors.newFixedThreadPool(urls.size());

    var futures = new ArrayList<Future<String>>();
    urls.forEach(url -> {
        futures.add(
            executor.submit(() -> getResponse(url)) // performs a http request with timeout and returns the response (or null in the case of failure)
        );
    });

    var failed = new ArrayList<Future<String>>();
    while (failed.size() < futures.size()) {
        for(var f: futures) {
            if(f.isDone() && !failed.contains(f)) {
                if(f.get() == null) {
                    failed.add(f);
                }
                else {
                    executor.shutdownNow();
                    return f.get();
                }
            }
        }
    }

    throw new RuntimeException("No response.");
}

This approach seems to be working. However, given that this method is called frequently, and the fact that creating Thread Pools is an expensive task (and I am creating one with each method call), I wonder whether I can somehow improve this approach or find other solutions that are faster and have less computational cost.

Any suggestions are welcome. Thank you in advance.

CodePudding user response:

First of all, use executor.invokeAny(). It will wait until one of the tasks is successfully completed and cancel the rest (so will replace almost all your code above).

About creating thread pool on every method call - sometimes it makes sense. But at the beginning, I would start with the common pool (ForkJoinPool.commonPool()) or create your own static (==reusable) pool and only replace it with pool-per-method-call after analyzing performance.

CodePudding user response:

Optimization 1: Create a static FixedThreadPool.

You can determine the thread-pool size by: (No. of concurrent calls to the fetchFastest method) x (urls.size())

This is better as the GC will not have clean the thread-pool instances that you created on each function call and the the static FixedThreadPool will be re-used.

Optimization 2: Interrupt threads that are no longer needed.

Interrupt all other threads once you get the first response. This is will save CPU cycles on busy wait and compute(if any).

  • Related