Let’s compare RxJava, Project Reactor, Spring WebFlux, and Akka Streams, each with simple Cold vs Hot observable examples.
🧪 Common Goal:
We want to emit a few numbers (1, 2, 3) and see the difference between:
- Cold stream: each subscriber gets its own sequence.
- Hot stream: all subscribers share the same source.
1️⃣ RxJava (Cold & Hot)
✅ Cold:
Observable<Integer> cold = Observable.just(1, 2, 3);
cold.subscribe(i -> System.out.println("Sub 1: " + i));
cold.subscribe(i -> System.out.println("Sub 2: " + i));
✅ Hot (PublishSubject):
PublishSubject<Integer> hot = PublishSubject.create();
hot.subscribe(i -> System.out.println("Sub 1: " + i));
hot.onNext(1);
hot.onNext(2);
hot.subscribe(i -> System.out.println("Sub 2: " + i)); // joins late
hot.onNext(3);
2️⃣ Project Reactor (Cold & Hot)
✅ Cold:
Flux<Integer> cold = Flux.just(1, 2, 3);
cold.subscribe(i -> System.out.println("Sub 1: " + i));
cold.subscribe(i -> System.out.println("Sub 2: " + i));
✅ Hot:
Sinks.Many<Integer> hot = Sinks.many().multicast().onBackpressureBuffer();
Flux<Integer> flux = hot.asFlux();
flux.subscribe(i -> System.out.println("Sub 1: " + i));
hot.tryEmitNext(1);
hot.tryEmitNext(2);
flux.subscribe(i -> System.out.println("Sub 2: " + i));
hot.tryEmitNext(3);
3️⃣ Spring WebFlux (Cold & Hot)
📦 Spring WebFlux is built on top of Project Reactor, so it behaves similarly.
✅ Cold in Controller:
@GetMapping("/cold")
public Flux<Integer> coldStream() {
return Flux.just(1, 2, 3); // each HTTP request gets its own sequence
}
✅ Hot using Sinks.Many in Controller:
Sinks.Many<Integer> hotSource = Sinks.many().multicast().onBackpressureBuffer();
@GetMapping("/hot")
public Flux<Integer> hotStream() {
return hotSource.asFlux(); // all clients share the same stream
}
@PostMapping("/emit")
public void emit(@RequestParam int value) {
hotSource.tryEmitNext(value);
}
GET /hot connects clients to a shared stream.
POST /emit?value=5 emits values to all connected clients.
4️⃣ Akka Streams (Cold & Hot)
Akka is more functional and actor-based, but supports both stream types.
✅ Cold:
val coldSource = Source(List(1, 2, 3))
coldSource.runForeach(i => println(s"Sub 1: $i"))
coldSource.runForeach(i => println(s"Sub 2: $i"))
✅ Hot using BroadcastHub:
val (pub, hotSource) = Source.queue .toMat(BroadcastHub.sink)(Keep.both).run()
hotSource.runForeach(i => println(s"Sub 1: $i"))
pub.offer(1)
pub.offer(2)
hotSource.runForeach(i => println(s"Sub 2: $i"))
pub.offer(3)
Late subscribers miss initial emissions — just like PublishSubject.
🧠 Summary Table:
| Feature | RxJava | Project Reactor | Spring WebFlux | Akka Streams |
|---|---|---|---|---|
| Cold API | Observable.just() | Flux.just() | Flux.just() | Source(List(...)) |
| Hot API | PublishSubject | Sinks.many() | Sinks.many() in ctrl | BroadcastHub |
| Language | Java | Java | Java + Spring | Scala / Java |
| Backpressure | ✅ (Flowable) | ✅ (Flux) | ✅ (Flux) | ✅ |
| Streams Model | Pull + push hybrid | Fully push | Reactive Streams Spec | Streams + Actors |
Would you like to:
- Run one of these live in a small project?
- Add delay/scheduling to better see stream behavior?
- Combine them with database or UI events?