Difference between a Future and a Mono
The greatest difference is that a Mono<T>
can be fully lazy, whereas when you get hold of a Future<T>
, the underlying processing has already started.
With a typical cold Mono
, nothing happens until you subscribe()
to it, which makes it possible to pass the Mono
around in the application and enrich it with operators along the way, before even starting the processing.
It is also far easier to keep things asynchronous using a Mono
compared to a Future
(where the API tends to drive you to call the blocking get()
).
Finally, compared to both Future
and CompletableFuture
, the composition aspect is improved in Mono
with the extensive vocabulary of operators it offers.
Producer and consumer can communicate in 2 ways: synchronous and asynchronous.
In synchronous (pull-based) way, consumer is a thread and some intermediate communicator object is used. Usually it is a blocking queue. In special case, when only single value is passed during the whole producer-consumer communication, a communicator which implements interface Future
can be used. This way is called synchronous, because the consumer calls communicating method like Future.get()
and that methods waits until the value is available, and then returns that value as a result. That is, requesting the value, and receiving it, are programmed in the same statement, though these actions can be separated in time.
The drawback of synchronous communication is that when the consumer waits for the requested value, it wastes considerable amount of memory for it's thread stack. As a result, we can have only limited number of actions which wait for data. For example, it could be internet connections serving multiple clients. To increase that number, we can represent consumer not as a thread, but as some relatively small object, with methods called by the producer or communicator when datum for consumer is available. This way is called asynchronous. It is split in 2 actions: request to producer to pass data and passing that data to consumer. This is asynchronous (push-based) method.
Now the reply to the question is: Future
is able to act as a synchronous communicator only (with get
methods), and Mono
can be used both as synchronous communicator (with block
methods) and as an asynchronous one (with subscribe
methods).
Note that java.util.concurrent.CompletableFuture
can also act both as synchronous and asynchronous communicator. Why to have similar means to do the same thing? This phenomenon is called not invented here.