Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

CompletableFuture supplyAsync

Tags:

I've just started exploring some concurrency features of Java 8. One thing confused me a bit is these two static methods:

CompletableFuture<Void> runAsync(Runnable runnable)  CompletableFuture<U> supplyAsync(Supplier<U> supplier) 

Do anyone know why they choose to use interface Supplier? Isn't it more natural to use Callable, which is the analogy of Runnable that returns a value? Is that because Supplier doesn't throw an Exception that couldn't be handled?

like image 696
Tuan Do Avatar asked Mar 28 '16 05:03

Tuan Do


1 Answers

Short answer

No, it's not more natural to use Callable instead of Supplier in CompletableFuture.supplyAsync. The argument is almost entirely about semantics, so it's OK if you still feel unconvinced afterwards.

Long answer

The Callable and Supplier functional interfaces/SAM types are practically equivalent in function (pardon the pun), but their origin and intended use differ.

Callable was created as part of the java.util.concurrent package. That package came before the vast changes around lambda expressions in Java 8 and originally concentrated on a range of tools that helped you write concurrent code, without deviating much from the classic model of hands-on multithreading.

The main purpose of Callable was to abstract an action that can be executed in a different thread and that returns a result. From Callable's Javadoc:

The Callable interface is similar to Runnable, in that both are designed for classes whose instances are potentially executed by another thread.

Supplier was created as part of the java.util.function package. That package came as an integral part of the aforementioned changes in Java 8. It provides common functional types that can be targeted by lambda expressions and method references.

One such type is a function without parameters that returns a result (i.e. a function that supplies some type or a Supplier function).

So why Supplier and not Callable?

CompletableFuture is part of additions to the java.util.concurrent package that were inspired by the aforementioned changes in Java 8 and that allow the developer to construct his code in a functional, implicitly parallelizable manner, instead of explicitly handling concurrency within it.

Its supplyAsync method needs a way to provide a result of a specific type and its more interested in this result, and not in the action taken to reach this result. It also doesn't necessarily care about exceptional completion (also see the What about the... paragraph below).

Still, if Runnable is used for no-parameters, no-result functional interface, shouldn't Callable be used for no-parameters, single-result functional interface?

Not necessarily.

An abstraction for a function that does not have a parameter and does not return a result (and therefore operates entirely through side effects on outside context) was not included in java.util.function. This means that (somewhat annoyingly) Runnable is used wherever such a functional interface is needed.

What about the checked Exception that can be thrown byCallable.call()?

It's a small sign of the intended semantic difference between Callable and Supplier.

A Callable is an action that can be executed in another thread, and that allows you to inspect its side effects as a result of its execution. If all goes well, you get a result of a specific type, but because exceptional situations can arise when executing some actions (especially in multithreaded context), you may also want to define and handle such exceptional situations.

A Supplier on the other hand is a function on which you rely for supplying objects of some type. Exceptional situations should not necessarily be made your responsibility as the direct consumer of the Supplier. This is so because:

  1. ...functional interfaces are often used for defining a specific stage in a multi-stage process for creating or mutating data, and handling Exceptions can be a separate stage, in case you care
  2. ...explicitly handling Exceptions significantly reduces the expressive powers of functional interfaces, lambda expressions and method references
like image 85
Dimitar Dimitrov Avatar answered Sep 30 '22 10:09

Dimitar Dimitrov