Reputation: 4275
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
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
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