Java 9 new Features: Reactive Streams

Java 9 is set to release in July 2017, and it will come with a list of new and revised features, methods, and other elements.

In the following post we take a deeper look into JEP 266, go over the improvements it has to offer. So let's start ;)

Reactive Streams

When Reactive Streams started their work, following the path established by the Reactive Manifesto, they indicated that their aim was to provide "a standard for asynchronous stream processing with non-blocking back pressure". The main challenge, however, wasn't to find a solution, in particular since there were already a number of them out there. The challenge was to coalesce the different existing patterns into a common one to maximise interoperability. More precisely, the objective of Reactive Streams was “to find a minimal set of interfaces, methods and protocols that will describe the necessary operations and entities to achieve the goal—asynchronous streams of data with non-blocking back pressure”.

The concept of "back pressure" is key. When an asynchronous consumer subscribes to receive messages from a producer, it will typically provide some form of callback method to be invoked whenever a new message becomes available. If the producer emits messages at a higher rate than the consumer can handle, the consumer could be forced to seize an increasing amount of resources and potentially crash. In order to prevent this, a mechanism is needed by which consumers can notify producers that the message rate needs to be reduced. Producers can then adopt one of multiple strategies to achieve this. This mechanism is called back pressure.

Blocking back pressure is easy to achieve. If, for instance, producer and consumer are running under the same thread, the execution of one will block the execution of the other. This means that, while the consumer is being executed, the producer cannot emit any new messages, and therefore a natural way to balance input and output occurs. However, there are scenarios where blocking back pressure is undesirable (for instance when a producer has multiple consumers, not all of them consuming messages at the same rate) or simply unattainable (for instance when consumer and producer run in different environments). In these cases it is necessary that the back pressure mechanism works in a non-blocking way.

The way to achieve non-blocking back pressure is to move from a push strategy, where the producer sends messages to the consumer as soon as these are available, to a pull strategy, where the consumer requests a number of messages to the producer and this sends only up to this amount, waiting for further requests before sending any more.

JEP 266: More Concurrency Updates

The Flow APIs in JDK 9 correspond to the Reactive Streams Specification, which is a defacto standard. JEP 266 contains a minimal set of interfaces that capture the heart of asynchronous publication and subscription. The hope is that in the future 3rd parties will implement them and thus convene on a shared set of types.

And here they are:

Publisher

Produces items for subscribers to consume. The only method is subscribe(Subscriber), whose purpose should be obvious.

Subscriber

Subscribes to publishers (usually only one) to receive items (via method onNext(T)), error messages (onError(Throwable)), or a signal that no more items are to be expected (onComplete()). Before any of those things happen, though, the publisher calls onSubscription(Subscription).

Subscription

The connection between a single publisher and a single subscriber. The subscriber will use it to request more items (request(long)) or to sever the connection (cancel()).

The flow is as follows:

  • Create a Publisher and a Subscriber.
  • Subscribe the subscriber with Publisher::subscribe.
  • The publisher creates a Subscription and calls Subscriber::onSubscription with it so the subscriber can store the subscription.
  • At some point the subscriber calls Subscription::request to request a number of items.
  • The publisher starts handing items to the subscriber by calling Subscriber::onNext. It will never publish more than the requested number of items.
  • The publisher might at some point be depleted or run into trouble and call Subscriber::onComplete orSubscriber::onError, respectively.
  • The subscriber might either continue to request more items every now and then or cut the connection by calling Subscription::cancel.

All of this is pretty straight forward, maybe with the exception of Subscription::request. Why would the subscriber need to do that? This is the implementation of back pressure explained above ;)

Note that the JDK provides only the interfaces and no implementations (with the exception of SubmissionPublisher)! There is also no move towards creating publishers for asynchronous tasks like walking the file system. All of the described interfaces are inner types of the class Flow, which has a good introductory documentation. So in order to actually use a reactive API, at some point, you should use an implementation. Current notable implementations of this specification on the JVM are Reactive Streams, Project Reactor (which will be integrated in Spring 5), Akka Streams, and RxJava.