15

I am new to the Java 8 concurrency features such as CompletableFuture and I hope you can help to get started with the following use case.

There is a service called TimeConsumingServices that provides time consuming operations which I'd like to run in parallel since all of them are independent.

interface TimeConsumingService {

  default String hello(String name) {
    System.out.println(System.currentTimeMillis() + " > hello " + name);
    return "Hello " + name;
  }
  default String planet(String name) {
    System.out.println(System.currentTimeMillis() + " > planet " + name);
    return "Planet: " + name;
  }
  default String echo(String name) {
    System.out.println(System.currentTimeMillis() + " > echo " + name);
    return name;
  }

  default byte[] convert(String hello, String planet, String echo) {
    StringBuilder sb = new StringBuilder();
    sb.append(hello);
    sb.append(planet);
    sb.append(echo);
    return sb.toString().getBytes();
  }
}

So far I implemented the following example and I have managed to call all three service methods in parallel.

public class Runner implements TimeConsumingService {

  public static void main(String[] args) {
    new Runner().doStuffAsync();
  }

  public void doStuffAsync() {
    CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> this.hello("Friend"));
    CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> this.planet("Earth"));
    CompletableFuture<String> future3 = CompletableFuture.supplyAsync(() -> this.echo("Where is my echo?"));

    CompletableFuture.allOf(future1, future2, future3).join();
  }
}

Is there a way to collect the return values of each service call and invoke the method byte[]‘ convert(String, String, String)?

saw303
  • 8,051
  • 7
  • 50
  • 90

3 Answers3

18

To combine the result after you have returned them all you can do something like this

CompletableFuture<byte[]> byteFuture = CompletableFuture.allOf(cf1, cf2, cf3)
                     .thenApplyAsync(aVoid -> convert(cf1.join(), cf2.join(), cf3.join()));
byte[] bytes = byteFuture.join();

This will run all of your futures, wait for them all to complete, then as soon as they are all finished will call your convert method you mention.

Ash
  • 2,562
  • 11
  • 31
5

After join you can simply get() values from future1 like:

String s1 = future1.get()

and so on

user2377971
  • 1,442
  • 4
  • 21
  • 30
  • But if I call `future1.get(); future2.get(); future3.get();` they aren't called in parallel, aren't they? – saw303 Feb 28 '17 at 09:17
  • 3
    When you call future1.get() then you just get already computed result. – Dmitry Gorkovets Feb 28 '17 at 09:17
  • 2
    Yes getting results is not called in parallel but all methods will be computed asynchronously. You can check it with adding `TimeUnit.seconds(2).sleep()`; after join method. You will see that `System.out.println` was called before that sleep. – user2377971 Feb 28 '17 at 09:20
  • @saw303 Consider storing your `Future`s in a List rather than as individual variables. Then you can loop through them to consume the results. – slim Feb 28 '17 at 09:29
  • You can check my old question about `CompletableFuture` and processing it with stream http://stackoverflow.com/questions/41392286/java-8-completablefuture-stream-and-timeouts – user2377971 Feb 28 '17 at 09:32
1

You can combine them using thenCombine() method if there is only 3 futures to complete:

final CompletableFuture<byte[]> byteFuture = future1.thenCombine(future2, (t, u) -> {
    StringBuilder sb = new StringBuilder();
    sb.append(t);
    sb.append(u);
    return sb.toString();
}).thenCombine(future3, (t, u) -> {
    StringBuilder sb = new StringBuilder();
    sb.append(t);
    sb.append(u);
    return sb.toString();
}).thenApply(s -> s.getBytes());

try {
    final byte[] get = byteFuture.get();
} catch (InterruptedException | ExecutionException ex) {
}
MBec
  • 2,172
  • 1
  • 12
  • 15