Back to Reactive & Flow API
Q1Question 1 of 2

What is the JDK Flow API?

Key Concepts
  • Flow API is four nested interfaces inside java.util.concurrent.Flow, added in Java 9.
  • Publisher produces items; Subscriber consumes them via onSubscribe, onNext, onError, onComplete.
  • Subscription is the private channel: request(n) pulls demand, cancel() stops the stream.
  • Processor is both Subscriber and Publisher, used for transformation stages in a pipeline.
  • Backpressure means the consumer controls the pace by calling request(n) so the producer never floods it.
  • SubmissionPublisher is the JDK's ready-made Publisher backed by ForkJoinPool with built-in buffering.
  • The Flow API has no operators; libraries like Reactor and RxJava add map, filter, and flatMap on top.

Definition

Flow API is the JDK's four-interface Reactive Streams contract where the subscriber calls `request(n)` to pull demand so a fast publisher never floods a slow consumer.
  • Flow.Publisher – a source of items with a single method subscribe(Subscriber) that starts the stream.
  • Flow.Subscriber – a consumer with four callbacks: onSubscribe, onNext, onError, onComplete.
  • Flow.Subscription – the private channel between one publisher and one subscriber, exposing request(long n) for demand and cancel() to stop.
  • Flow.Processor – both a subscriber and a publisher, used for transformation stages in a pipeline.
  • Backpressure – the consumer calls request(n) to say how many items it can handle; the publisher must never send more onNext calls than the total requested.

Imagine Rajesh is a fast teller at National Bank of India who can stamp a thousand cheques a minute, and Priya is an auditor who can only verify ten cheques a minute. If Rajesh just keeps throwing stamped cheques at Priya, her desk overflows and cheques fall on the floor (memory overflow). Instead, Priya shouts "send me ten" and Rajesh sends exactly ten. When she finishes, she shouts "send me ten more". That shouting is request(n). Priya controls the pace, not Rajesh. This is backpressure.

Understanding the Concepts

What is the JDK Flow API?

The Flow class is a container for four nested interfaces added in Java 9. They are the JDK's standard adoption of the Reactive Streams specification, which defines a protocol for asynchronous stream processing with non-blocking backpressure.

What is a Publisher?

A Flow.Publisher is a source of items with a single method subscribe(Subscriber s). When you subscribe, the publisher hands back a Subscription by calling the subscriber's onSubscribe method.

JAVA
package com.bank.reactive;

import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;

public class TransactionFeedDemo {

    public static void main(String[] args) throws InterruptedException {
        SubmissionPublisher<String> publisher = new SubmissionPublisher<>();
        publisher.subscribe(new AuditSubscriber());

        publisher.submit("TXN-001: Debit Rs.50000 from SAV2001");
        publisher.submit("TXN-002: Credit Rs.50000 to CUR3001");
        publisher.close();

        Thread.sleep(1000); // let async processing complete
    }
}

What is a Subscriber?

A Flow.Subscriber has four callbacks. onSubscribe receives the subscription and sets initial demand. onNext delivers one item. onError is called once if the stream fails. onComplete is called once when the stream ends normally.

JAVA
package com.bank.reactive;

import java.util.concurrent.Flow;

public class AuditSubscriber implements Flow.Subscriber<String> {

    private Flow.Subscription subscription;

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1);  // start with demand for 1 item
    }

    @Override
    public void onNext(String item) {
        System.out.println("Auditing: " + item);
        subscription.request(1);  // request next item after processing
    }

    @Override
    public void onError(Throwable throwable) {
        System.err.println("Stream error: " + throwable.getMessage());
    }

    @Override
    public void onComplete() {
        System.out.println("Audit stream completed.");
    }
}
TEXT
Auditing: TXN-001: Debit Rs.50000 from SAV2001
Auditing: TXN-002: Credit Rs.50000 to CUR3001
Audit stream completed.

What is a Subscription?

A Flow.Subscription is the private channel between one publisher and one subscriber. request(long n) tells the publisher the subscriber is ready for n more items. cancel() tells the publisher to stop sending.

What is a Processor?

A Flow.Processor is both a subscriber and a publisher. It consumes items of type T and republishes transformed items of type R. Processors let you build pipelines like: raw feed, then fee enrichment, then fraud scoring.

JAVA
package com.bank.reactive;

import java.util.concurrent.Flow;
import java.util.concurrent.SubmissionPublisher;

public class FeeEnrichmentProcessor
        extends SubmissionPublisher<String>
        implements Flow.Processor<String, String> {

    private Flow.Subscription subscription;

    @Override
    public void onSubscribe(Flow.Subscription subscription) {
        this.subscription = subscription;
        subscription.request(1);
    }

    @Override
    public void onNext(String item) {
        String enriched = item + " [Fee: Rs.10]";
        submit(enriched);           // publish downstream
        subscription.request(1);    // request next from upstream
    }

    @Override
    public void onError(Throwable throwable) {
        closeExceptionally(throwable);
    }

    @Override
    public void onComplete() {
        close();
    }
}

How does demand-based backpressure work?

The model is a hybrid of pull and push. The subscriber pulls by calling request(n) to state demand. Once demand exists, the publisher pushes items via onNext without the subscriber asking individually. The publisher must never send more onNext calls than the total requested.

What is SubmissionPublisher?

The JDK ships SubmissionPublisher, a ready-made Publisher backed by a bounded buffer and a ForkJoinPool. You rarely write a publisher from scratch. You use SubmissionPublisher and call its submit(item) method, and it handles the request(n) accounting and buffering.

How is Flow API different from a plain Observer or Stream?

A plain Observer has no backpressure. A Java Stream is pull-based and synchronous. The Flow API adds demand-based backpressure so a fast producer never overwhelms a slow consumer.

| Feature | Observer | Java Stream | Flow API |
|---------|----------|------------|----------|
| Backpressure | No | No | Yes, via request(n) |
| Async | Depends | No | Yes |
| Operators | No | map, filter | None (use Reactor/RxJava) |

What are common interview follow-up questions?

  • How does SubmissionPublisher handle slow subscribers?
  • What is the difference between hot and cold publishers?
  • How does the Flow API relate to Reactor's Flux and Mono?

Visual Overview

DIAGRAM
  ┌─────────────┐         ┌──────────────┐         ┌─────────────┐
  │  Publisher   │         │  Processor   │         │ Subscriber  │
  │ (TXN Feed)  │────────▶│ (Fee Enrich) │────────▶│  (Auditor)  │
  └─────────────┘         └──────────────┘         └─────────────┘
        │                       │                        │
        │    subscribe()        │     subscribe()        │
        │◀──────────────────────│◀───────────────────────│
        │                       │                        │
        │    onSubscribe(sub)   │     onSubscribe(sub)   │
        │──────────────────────▶│───────────────────────▶│
        │                       │                        │
        │                       │     request(1)         │
        │     request(1)        │◀───────────────────────│
        │◀──────────────────────│                        │
        │                       │                        │
        │     onNext(item)      │     onNext(enriched)   │
        │──────────────────────▶│───────────────────────▶│
        │                       │                        │
        │     onComplete()      │     onComplete()       │
        │──────────────────────▶│───────────────────────▶│

Key Takeaway

The Flow API arrived in Java 9 as the JDK's standard adoption of the Reactive Streams specification. It is deliberately tiny: just four nested interfaces inside java.util.concurrent.Flow. A Publisher is a source you subscribe to. A Subscriber has four callbacks. A Subscription carries the demand signal. A Processor chains transformation stages.

The single idea that makes this different from a plain observer is demand-based backpressure. The consumer calls request(n) to say how many items it is ready for, and the publisher is contractually forbidden from sending more than requested.

In my banking work this solved a real problem. Our real-time transaction feed produced faster than the fraud-verification desk could consume. Without backpressure the verifier's queue grew until the service ran out of memory. With a Flow pipeline, the verifier requested items only as fast as it could process them, and the whole pipeline naturally ran at the speed of the slowest stage.

The nuance is that the Flow API is only the contract. The JDK intentionally shipped no operators like map or filter, because those belong to libraries like Reactor and RxJava which implement the very same interfaces.

Remember one sentence: the Flow API is four interfaces where the subscriber calls request(n) to pull demand, so a fast publisher never floods a slow consumer.

---

Share
Use ← → arrow keys to navigate