Method call to Future.get() blocks. Is that really desirable?

TheMonkWhoSoldHisCode picture TheMonkWhoSoldHisCode · Jun 27, 2015 · Viewed 58.4k times · Source

Please read the question carefully before marking this as duplicate.

Below is the snippet of the pseudo code. My question is- Does the below code not defeat the very notion of parallel asynchronous processing?

The reason I ask this is because in the below code the main thread would submit a task to be executed in a different thread. After submitting the task in the queue, it blocks on Future.get() method for the task to return the value. I would rather have the task executed in the main thread rather than submitting to a different thread and waiting for the results. What is that I gained by executing the task in a new thread?

I am aware that you could wait for a limited time etc, but then what if I really care about the result? The problem gets worse if there are multiple tasks to be executed. It seems to me that we are just doing the work synchronously. I am aware of the Guava library which provides a non blocking listener interface. But I am interested to know if my understanding is correct for the Future.get() API. If it is correct, why is the Future.get() designed to block thereby defeating the whole process of parallel processing?

Note - For the record, I use JAVA 6

public static void main(String[] args){

private ExectorService executorService = ...

Future future = executorService.submit(new Callable(){
    public Object call() throws Exception {
        System.out.println("Asynchronous Callable");
        return "Callable Result";
    }
});

System.out.println("future.get() = " + future.get());
}

Answer

John picture John · Jun 27, 2015

Future offers you method isDone() which is not blocking and returns true if computation has completed, false otherwise.

Future.get() is used to retrieve the result of computation.

You have a couple of options:

  • call isDone() and if the result is ready ask for it by invoking get(), notice how there is no blocking
  • block indefinitely with get()
  • block for specified timeout with get(long timeout, TimeUnit unit)

The whole Future API thing is there to have easy way obtaining values from threads executing parallel tasks. This can be done synchronously or asynchronously if you prefer, as described in bullets above.

UPDATE WITH CACHE EXAMPLE

Here is a cache implementation from Java Concurrency In Practice, an excellent use case for Future.

  • If the computation is already running, caller interested in result of computation will wait for computation to finish
  • If the result is ready in the cache, caller will collect it
  • if the result is not ready and computation has not started yet, caller will start computation and wrap result in Future for other callers.

This is all easily achieved with Future API.

package net.jcip.examples;

import java.util.concurrent.*;
/**
 * Memoizer
 * <p/>
 * Final implementation of Memoizer
 *
 * @author Brian Goetz and Tim Peierls
 */
public class Memoizer <A, V> implements Computable<A, V> {
    private final ConcurrentMap<A, Future<V>> cache
            = new ConcurrentHashMap<A, Future<V>>();
    private final Computable<A, V> c;

public Memoizer(Computable<A, V> c) {
    this.c = c;
}

public V compute(final A arg) throws InterruptedException {
    while (true) {

        Future<V> f = cache.get(arg);
        // computation not started
        if (f == null) {
            Callable<V> eval = new Callable<V>() {
                public V call() throws InterruptedException {
                    return c.compute(arg);
                }
            };

            FutureTask<V> ft = new FutureTask<V>(eval);
            f = cache.putIfAbsent(arg, ft);
            // start computation if it's not started in the meantime
            if (f == null) {
                f = ft;
                ft.run();
            }
        }

        // get result if ready, otherwise block and wait
        try {
            return f.get();
        } catch (CancellationException e) {
            cache.remove(arg, f);
        } catch (ExecutionException e) {
            throw LaunderThrowable.launderThrowable(e.getCause());
        }
    }
  }
}