asmhatre f506b01389 Add virtual-threads-benchmark: re-run Spring Boot 4.1 / JDK 25 benchmarks, JEP 491 pinning fixed
Companion module for the rewritten post 'Virtual Threads on Spring Boot 4.1: The Benchmarks,
Re-Run, and the Pinning Advice That Expired', retitled and re-benchmarked on Boot 4.1.1 /
JDK 25.0.4.1 (the original post was written against Boot 3.4 / JDK 21). Covers: I/O-bound and
CPU-bound throughput (platform vs virtual threads, including a JIT-warmup benchmarking bug this
build caught and fixed), JEP 491 proof that synchronized no longer pins a virtual thread's
carrier across a blocking call as of JDK 24 (obsoleting the old avoid-synchronized advice),
proof that -Djdk.tracePinnedThreads=full is inert on JDK 25, and JEP 506's finalized ScopedValue
API (JDK 25 GA, no --enable-preview, and a different shape than the old preview API). Kept as
its own module rather than a new top-level repository, alongside the existing async/ module,
which already has a stronger dual-JDK JEP 491 proof that this module's docs cross-link to
instead of duplicating.
2026-09-18 08:52:59 +00:00
2026-09-01 23:32:16 +05:30
2026-09-01 23:32:16 +05:30
2026-09-01 23:32:16 +05:30
2026-09-01 23:32:16 +05:30
2026-09-01 23:32:16 +05:30

spring-async-demo

Companion code for the asynchronous execution and scheduling series on ankurm.com. Each directory is a self-contained Maven project for one article, with its own pom.xml, its own numbered documentation chapters, and its own captured output under docs/output/ — regenerated by that module's scripts/run-all.sh, never typed by hand.

Module Article What it demonstrates
async/ @Async in Spring Boot 4: Executors, Virtual Threads and the Self-Invocation Trap Which thread a method actually ran on, in every case where the answer is not the one you expect
scheduling/ @Scheduled, ShedLock and Distributed Cron: Scheduling That Survives Three Replicas Three replicas against one database running the same job three times, then one row and one conditional UPDATE fixing it

Common ground

All modules target the same verified stack: JDK 25 (Temurin 25.0.4.1+1), Spring Boot 4.1.1, Spring Framework 7.0.9. Versions were read from maven-metadata.xml on Maven Central and from Boot's own spring-boot-dependencies POM, rather than from release announcements.

Everything is asserted by a test and captured to a file. The measurement is nearly always the same one: the name of the thread that ran the work, returned by the code itself. Timing cannot tell a fast synchronous call from an asynchronous one, which is why @Async failures survive so long in production.

The two modules share a mechanism, which is why they live together: both @Async and ShedLock's default PROXY_METHOD intercept mode are Spring AOP proxies. Every proxy limitation the async module measures — self-invocation, final methods — applies unchanged to a @SchedulerLock method, and silently produces an unlocked job rather than a synchronous one.

The scheduling module also needs a database. scheduling/scripts/postgres.sh unpacks a throwaway PostgreSQL 14 into target/ with no Docker and no root, which is how its transcripts were produced; docker-compose.yml is there for anyone who would rather use Docker.

Licence

MIT — see LICENSE.

S
Description
Companion code for the asynchronous execution and scheduling series on ankurm.com: @Async proxies, executors and virtual threads, and @Scheduled across three replicas with ShedLock. Spring Boot 4.1.1, JDK 25.
Readme MIT
245 KiB
Languages
Java 90.9%
Shell 8.6%
Dockerfile 0.5%