justik
justik

Reputation: 4275

Java, returning value from thread using Future

Suppose the following illustrative example. There is the class B involving some numerical procedures, for example the factorial. The computation runs in a separate thread:

public class B implements Callable <Integer> {
    private int n;
    
    public B(int n_) {n = n_;}

    public Integer call()  {return f();}
    
    public Integer f() {
            if (n == 1)  return 1;
            else {
                    int fn = 1;
                    for (int i = n; i > 1; i--) fn *= i;
                    return fn;
            }
    }
}

The next class A is using the factorial to evaluate the remainder r = x^n /n!

public class A {
    
    public double rem (double x, int n){
            B b = new B(n);
            ExecutorService es = Executors.newFixedThreadPool(5);
            Future <Integer> nf = es.submit(b);  //Factorial
            es.submit(()->
            {          
                    double r = 1;     //Remainder x^n/n     
                    for (int i = 1; i <= n; i++) r = r * x;
                    try { r = r / nf.get();}
                    catch (Exception e) {e.printStackTrace();}                 
                    return r;
            });                     
            return 0;
    }
}

How to ensure that rem() function returns the value after the submit() procedure has been finished? Unfortunately, this does not work:

    public static void main(String[] args) {
            A a = new A();
            double r = a.rem(0.5, 10);
    }

Is it necessary to run A in another thread and modify A so that:

public class A implements Callable <Double> {
    private int n;
    private double x;
    public A(double x_, int n_) {x = x_; n = n_;}
    public Double call()  {return rem(x, n);}
    ....
 }

and run A.rem() in a separate thread ?

    public static void main(String[] args) {
            A a = new A(0.5, 10);
            ExecutorService es = Executors.newFixedThreadPool(5);
            Future <Double> nf = es.submit(a);  //Factorial
            double r = nf.get();
    }

Is there any simpler solution avoiding two different threads?

Could I ask for a short sample code?

Upvotes: 1

Views: 2794

Answers (2)

DavidGaray
DavidGaray

Reputation: 26

Callable objects implement the method get which returns the value calculated by the thread - have a look at the following link: https://blogs.oracle.com/corejavatechtips/using-callable-to-return-results-from-runnables

Upvotes: 0

Alexei Kaigorodov
Alexei Kaigorodov

Reputation: 13535

Using Future.get() inside a task submitted to a thread pool is dangerous: current thread is blocked and cannot run other tasks. This may lead to thread starvation - a specific kind of deadlock.

The correct approach is to make acyclic graph where each node is an asynchronous function call of type CompletableFuture, which runs only after all arguments are calculated. Only the general result is extracted using Future.get() called on the main thread.

This is an example of such a graph, made close to what you wanted to implement: first, functions factorial and power run in parallel. As soon as they both complete, function to compute reminder is called.

public static long fact(int n) {
    long res = 1;
    for (int i = n; i > 1; i--) res *= i;
    return res;
}

public static double pow(double base, int pow) {
    double r = 1;
    for (int i = 0; i < pow; i++) r *= base;
    return r;
}

public static double rem(double val1, long val2) {
    return val1/val2;
}

public static void main(String[] args) throws ExecutionException, InterruptedException {
    ExecutorService es = Executors.newFixedThreadPool(5);
    double base = 0.5;
    int n = 10;
    CompletableFuture<Double> f1 = CompletableFuture.supplyAsync(() -> pow(base, n), es);
    CompletableFuture<Long> f2 = CompletableFuture.supplyAsync(() -> fact(n), es);
    CompletableFuture<Double> f3 = f1.thenCombineAsync(f2, (v1,v2)->rem(v1,v2), es);
    double r1 = f3.get();
    System.out.println("r1="+r1);
    // compare with the result of synchronous execution:
    double r2 = rem(pow(base, n), fact(n));
    System.out.println("r2="+r2);
}

Upvotes: 2

Related Questions