CompletableFuture<T> class: join() vs get()

JavaJava 8Completable Future

Java Problem Overview


What is the difference between the get() and join() methods of the CompletableFuture<T> class?

Below is the my code:

List<String> process() {

	List<String> messages = Arrays.asList("Msg1", "Msg2", "Msg3", "Msg4", "Msg5", "Msg6", "Msg7", "Msg8", "Msg9",
			"Msg10", "Msg11", "Msg12");
	MessageService messageService = new MessageService();
	ExecutorService executor = Executors.newFixedThreadPool(4);

	List<String> mapResult = new ArrayList<>();

	CompletableFuture<?>[] fanoutRequestList = new CompletableFuture[messages.size()];
	int count = 0;
	for (String msg : messages) {
		CompletableFuture<?> future = CompletableFuture
				.supplyAsync(() -> messageService.sendNotification(msg), executor).exceptionally(ex -> "Error")
				.thenAccept(mapResult::add);

		fanoutRequestList[count++] = future;
	}

	try {
		CompletableFuture.allOf(fanoutRequestList).get();
      //CompletableFuture.allOf(fanoutRequestList).join();
	} catch (InterruptedException | ExecutionException e) {
		// TODO Auto-generated catch block
		e.printStackTrace();
	}

	return mapResult.stream().filter(s -> !s.equalsIgnoreCase("Error")).collect(Collectors.toList());
}

I have tried with both methods but I see no difference in result.

Java Solutions


Solution 1 - Java

The only difference is how methods throw exceptions. get() is declared in Future interface as:

V get() throws InterruptedException, ExecutionException;

The exceptions are both checked exceptions which means they need to be handled in your code. As you can see in your code, an automatic code generator in your IDE asked to create try-catch block on your behalf.

try {
  CompletableFuture.allOf(fanoutRequestList).get() 
} catch (InterruptedException | ExecutionException e) {
  // TODO Auto-generated catch block
  e.printStackTrace();
}

The join() method doesn't throw checked exceptions.

public T join()

Instead it throws unchecked CompletionException. So you do not need a try-catch block and instead you can fully harness exceptionally() method when using the disscused List<String> process function

CompletableFuture<List<String>> cf = CompletableFuture
    .supplyAsync(this::process)
    .exceptionally(this::getFallbackListOfStrings) // Here you can catch e.g. {@code join}'s CompletionException
    .thenAccept(this::processFurther);

You can find both get() and join() implementation here.

Solution 2 - Java

In addition to the answer provided by Dawid, the get method is available in two flavors:

get()
get(Long timeout, TimeUnit timeUnit) 

The second get takes wait time as an argument and waits for at most the provided wait time.

try {
    System.out.println(cf.get(1000, TimeUnit.MILLISECONDS));
} catch (InterruptedException | ExecutionException | TimeoutException ex) {
    ex.printStackTrace();
}

You can refer to this documentation for more information.

  1. join() is defined in CompletableFuture whereas get() comes from interface Future
  2. join() throws unchecked exception whereas get() throws checked exceptions
  3. You can interrupt get() and then throws an InterruptedException
  4. get() method allows to specify the maximum wait time

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionNomeswaranView Question on Stackoverflow
Solution 1 - JavaDawid WróblewskiView Answer on Stackoverflow
Solution 2 - JavaPrajView Answer on Stackoverflow