Lesson 1 · Predict → Run → Prove
You understand the mechanism. Now put a number on it before you run anything — that's the difference between knowing and intuition.
One request to /orders/1 fans out to two downstream services concurrently, each stubbed at a fixed 50 ms. In broken mode, every supplyAsync(task) lands on the common pool, whose parallelism is availableProcessors() − 1. We'll pretend to be a 4-core container with -XX:ActiveProcessorCount=4.
From Little's Law, concurrency = throughput × latency, rearranged to predict the cap:
throughput ≈ workers × (1 / latency) ÷ calls_per_request
Fill in the three numbers for broken / 4 cores:
4 − 10.05 s2The stack is already up (WireMock, Prometheus, Grafana) and the jar is pre-built. In a terminal:
cd ~/Desktop/thread-starvation-course/demo
./run-app.sh broken 4
Watch the startup banner — it prints availableProcessors() and common-pool parallelism, then its own throughput estimate. Then apply load from a second terminal:
./load.sh 50 60 # 50 concurrent clients, 60 seconds
Open Grafana :3000 → dashboard Thread Starvation — ForkJoin Common Pool. Read Row 1: throughput climbs, then flatlines. Compare the flatline to your locked-in guess above.
While the load runs, dump the threads:
jcmd $(jps | grep starvation | cut -d' ' -f1) Thread.print | grep -A5 commonPool
You'll see three ForkJoinPool.commonPool-worker threads. What state are they in — and is that a problem?
socketRead0, waiting on the 50 ms HTTP response. That is not a bug and not a deadlock — sleeping is the normal state for a thread doing network I/O. The problem isn't that they sleep; it's that there are only three of them for the entire JVM and ForkJoin spawns no compensating thread to drain the queue while they wait. Hold onto this: in Lesson 3 we'll use it to tear apart the idea that a "blocked" thread and a "blocked" connection are the same thing.
CompletableFuture. Read the class-level paragraph on the default executor: async methods with no executor run on ForkJoinPool.commonPool(). That one sentence is the entire bug.
jcmd output and we'll read it together. When you're ready, ask for Lesson 2, where we read p50 vs p99 and watch the mean lie to you.