{"id":2214,"date":"2026-07-07T01:48:50","date_gmt":"2026-07-06T23:48:50","guid":{"rendered":"https:\/\/kindsonthegenius.com\/blog\/spring-boot-3-java-21-virtual-threads-rest-api-tutorial\/"},"modified":"2026-08-27T18:06:07","modified_gmt":"2026-08-27T16:06:07","slug":"virtual-threads-rest-api-tutorial","status":"publish","type":"post","link":"https:\/\/kindsonthegenius.com\/blog\/virtual-threads-rest-api-tutorial\/","title":{"rendered":"Spring Boot 3 + Java 21: Virtual Threads and REST API Tutorial (2026)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><em>Learn how to use <strong>Java 21 virtual threads with Spring Boot 3<\/strong> to build REST APIs that handle thousands of concurrent I\/O-bound requests without thread-pool exhaustion.<\/em><\/p>\n\n\n<p><!-- ktg-updated-banner --><\/p>\n<div class=\"ktg-updated-banner\" style=\"margin: 1em 0; padding: 0.75em 1em; background: #eff6ff; border-left: 4px solid #3b82f6; border-radius: 4px;\">\n<p><strong>Updated July 7, 2026:<\/strong> Covers Spring Boot 3.3, Java 21 LTS, virtual thread configuration, a sample REST API, and load-testing tips. Part of the Spring Boot 2026 tutorial series.<\/p>\n<\/div>\n<h2>TL;DR<\/h2>\n<ul>\n<li>\n<p><strong>Java 21 virtual threads<\/strong> provide lightweight concurrency for handling large numbers of I\/O-bound requests.<\/p>\n<\/li>\n<li>\n<p><strong>Spring Boot 3.2+<\/strong> can enable virtual threads with a single <code>spring.threads.virtual.enabled=true<\/code> property.<\/p>\n<\/li>\n<li>\n<p>Virtual threads work especially well for <strong>blocking I\/O<\/strong>, such as database queries and calls to external services.<\/p>\n<\/li>\n<li>\n<p><strong>Structured concurrency<\/strong> can simplify parallel I\/O when a request needs data from multiple sources.<\/p>\n<\/li>\n<li>\n<p><strong>Benchmark your workload<\/strong> before switching. Virtual threads help with concurrency and I\/O, but they are not a replacement for optimizing CPU-heavy code.<\/p>\n<\/li>\n<\/ul>\n<p>This <strong>spring boot java 21 tutorial<\/strong> shows configuration, a working controller, and when virtual threads beat platform threads.<\/p>\n<p><strong>Prerequisites:<\/strong> Java basics and Spring Boot fundamentals. Complete <a href=\"https:\/\/kindsonthegenius.com\/blog\/java-progamming-for-beginners-lesson-1-introduction-to-java-and-intallation-of-netbeans\/\">Java Lesson 1<\/a> if you are new to Java.<\/p>\n<p><strong>Estimated time:<\/strong> 60\u201375 minutes. <strong>Difficulty:<\/strong> Intermediate.<\/p>\n<p><!-- ktg-article-toc --><\/p>\n<nav class=\"ktg-article-toc\" style=\"margin: 1.5em 0; padding: 1em 1.25em; background: #f8fafc; border: 1px solid #e2e8f0; border-radius: 8px;\" aria-label=\"Spring Boot Java 21 virtual threads tutorial table of contents\">\n<h2>Table of Contents<\/h2>\n<ol>\n<li><a href=\"#virtual-threads\">What are virtual threads?<\/a><\/li>\n<li><a href=\"#when-to-use\">When to use virtual threads in Spring Boot<\/a><\/li>\n<li><a href=\"#requirements\">What you need<\/a><\/li>\n<li><a href=\"#create-project\">Create the Spring Boot 3 project<\/a><\/li>\n<li><a href=\"#enable-virtual-threads\">Enable virtual threads<\/a><\/li>\n<li><a href=\"#rest-api\">Build a REST API with blocking I\/O<\/a><\/li>\n<li><a href=\"#structured-concurrency\">Structured concurrency example<\/a><\/li>\n<li><a href=\"#benchmark\">Benchmark platform vs virtual threads<\/a><\/li>\n<li><a href=\"#faq\">Frequently asked questions<\/a><\/li>\n<li><a href=\"#next-steps\">Next steps<\/a><\/li>\n<\/ol>\n<\/nav>\n<h2 id=\"virtual-threads\">What Are Virtual Threads?<\/h2>\n<p><strong>Virtual threads<\/strong> (Project Loom, finalized in Java 21) are lightweight threads managed by the JVM rather than the operating system. A single platform thread can run thousands of virtual threads, making them ideal for <strong>I\/O-bound<\/strong> workloads like HTTP clients, database queries, and file reads.<\/p>\n<figure style=\"margin: 1.5em 0; text-align: center;\"><img loading=\"lazy\" decoding=\"async\" style=\"max-width: 100%; height: auto; border-radius: 8px; border: 1px solid #dee2e6;\" src=\"https:\/\/kindsonthegenius.com\/blog\/wp-content\/uploads\/2026\/07\/java-21-virtual-threads-diagram.svg\" alt=\"Diagram comparing Java platform threads one-to-one with OS threads versus many virtual threads sharing carrier threads\" width=\"800\" height=\"380\" \/>\n<figcaption style=\"font-size: 0.85em; color: #666; margin-top: 0.5em;\">Platform threads vs Java 21 virtual threads \u2014 memory and scalability comparison<\/figcaption>\n<\/figure>\n<h2 id=\"when-to-use\">When to Use Virtual Threads in Spring Boot<\/h2>\n<table style=\"width: 100%; border-collapse: collapse; margin: 1em 0;\">\n<thead>\n<tr style=\"background: #f1f5f9;\">\n<th style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">Use virtual threads<\/th>\n<th style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">Stick with platform threads<\/th>\n<\/tr>\n<\/thead>\n<tbody>\n<tr>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">REST APIs with blocking JDBC\/JPA<\/td>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">CPU-heavy computation (image processing, ML inference)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">Microservices calling external HTTP APIs<\/td>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">Code using synchronized blocks on hot paths (pinning)<\/td>\n<\/tr>\n<tr>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">High concurrency, moderate latency tolerance<\/td>\n<td style=\"padding: 0.5em; border: 1px solid #e2e8f0;\">Legacy libraries not yet virtual-thread friendly<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<h2 id=\"requirements\">What You Need<\/h2>\n<ul>\n<li><strong>JDK 21<\/strong> (LTS) \u2014 download from <a href=\"https:\/\/adoptium.net\/\" target=\"_blank\" rel=\"noopener noreferrer\">Adoptium<\/a><\/li>\n<li><strong>Spring Boot 3.2+<\/strong> (3.3 recommended)<\/li>\n<li>Maven 3.9+ or Gradle 8+<\/li>\n<li>Optional: <code>hey<\/code> or Apache JMeter for load testing<\/li>\n<\/ul>\n<h2 id=\"create-project\">Step 1: Create the Spring Boot 3 Project<\/h2>\n<p>At <a href=\"https:\/\/start.spring.io\/\" target=\"_blank\" rel=\"noopener noreferrer\">start.spring.io<\/a>, select Java <strong>21<\/strong>, Spring Boot <strong>3.3.x<\/strong>, and add <strong>Spring Web<\/strong>. Name the project <code>virtual-threads-demo<\/code>.<\/p>\n<pre><code class=\"language-xml\">&lt;properties&gt;\n  &lt;java.version&gt;21&lt;\/java.version&gt;\n&lt;\/properties&gt;\n&lt;dependency&gt;\n  &lt;groupId&gt;org.springframework.boot&lt;\/groupId&gt;\n  &lt;artifactId&gt;spring-boot-starter-web&lt;\/artifactId&gt;\n&lt;\/dependency&gt;\n<\/code><\/pre>\n<figure style=\"margin: 1.5em 0; text-align: center;\"><img loading=\"lazy\" decoding=\"async\" style=\"max-width: 100%; height: auto; border-radius: 8px; border: 1px solid #dee2e6;\" src=\"https:\/\/kindsonthegenius.com\/blog\/wp-content\/uploads\/2026\/07\/spring-boot-java21-start-project.png\" alt=\"Spring Initializr showing Java 21 and Spring Boot 3.3 selected for new REST API project\" width=\"640\" height=\"400\" \/>\n<figcaption style=\"font-size: 0.85em; color: #666; margin-top: 0.5em;\">Spring Initializr \u2014 Java 21 + Spring Boot 3.3<\/figcaption>\n<\/figure>\n<h2 id=\"enable-virtual-threads\">Step 2: Enable Virtual Threads<\/h2>\n<p>Add one line to <code>application.properties<\/code>:<\/p>\n<pre><code class=\"language-properties\">spring.threads.virtual.enabled=true\n<\/code><\/pre>\n<p>Spring Boot 3.2+ configures Tomcat to use virtual threads for request handling automatically. No custom <code>Executor<\/code> bean is required for standard MVC controllers.<\/p>\n<figure style=\"margin: 1.5em 0; text-align: center;\"><img loading=\"lazy\" decoding=\"async\" style=\"max-width: 100%; height: auto; border-radius: 8px; border: 1px solid #dee2e6;\" src=\"https:\/\/kindsonthegenius.com\/blog\/wp-content\/uploads\/2026\/07\/spring-boot-virtual-threads-config.png\" alt=\"application.properties file with spring.threads.virtual.enabled=true highlighted in IDE\" width=\"640\" height=\"320\" \/>\n<figcaption style=\"font-size: 0.85em; color: #666; margin-top: 0.5em;\">Enable virtual threads with a single Spring Boot property<\/figcaption>\n<\/figure>\n<h2 id=\"rest-api\">Step 3: Build a REST API with Blocking I\/O<\/h2>\n<p>Create a controller that simulates I\/O latency \u2014 the classic use case for virtual threads:<\/p>\n<pre><code class=\"language-java\">@RestController\n@RequestMapping(\"\/api\/products\")\npublic class ProductController {\n\n    @GetMapping(\"\/{id}\")\n    public Product getProduct(@PathVariable Long id) throws InterruptedException {\n        \/\/ Simulate blocking database or HTTP call\n        Thread.sleep(200);\n        return new Product(id, \"Widget \" + id, 29.99);\n    }\n\n    @GetMapping\n    public List&lt;Product&gt; listProducts() throws InterruptedException {\n        Thread.sleep(100);\n        return List.of(\n            new Product(1L, \"Widget 1\", 19.99),\n            new Product(2L, \"Widget 2\", 29.99)\n        );\n    }\n\n    record Product(Long id, String name, double price) {}\n}\n<\/code><\/pre>\n<p>Run the app:<\/p>\n<pre><code class=\"language-bash\">mvn spring-boot:run\ncurl http:\/\/localhost:8080\/api\/products\/1\n<\/code><\/pre>\n<figure style=\"margin: 1.5em 0; text-align: center;\"><img loading=\"lazy\" decoding=\"async\" style=\"max-width: 100%; height: auto; border-radius: 8px; border: 1px solid #dee2e6;\" src=\"https:\/\/kindsonthegenius.com\/blog\/wp-content\/uploads\/2026\/07\/spring-boot-virtual-threads-curl-response.png\" alt=\"Terminal curl command returning JSON product response from Spring Boot REST API\" width=\"640\" height=\"300\" \/>\n<figcaption style=\"font-size: 0.85em; color: #666; margin-top: 0.5em;\">REST API response \u2014 each request runs on a virtual thread<\/figcaption>\n<\/figure>\n<h2 id=\"structured-concurrency\">Step 4: Structured Concurrency Example<\/h2>\n<p>Java 21&#8217;s structured concurrency lets you fan out parallel I\/O safely:<\/p>\n<pre><code class=\"language-java\">@GetMapping(\"\/aggregate\/{id}\")\npublic AggregatedProduct aggregate(@PathVariable Long id) throws Exception {\n    try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {\n        Subtask&lt;Product&gt; product = scope.fork(() -&gt; fetchProduct(id));\n        Subtask&lt;Reviews&gt; reviews = scope.fork(() -&gt; fetchReviews(id));\n\n        scope.join();\n        scope.throwIfFailed();\n\n        return new AggregatedProduct(product.get(), reviews.get());\n    }\n}\n<\/code><\/pre>\n<p>This pattern works well in Spring services that call multiple downstream APIs \u2014 common in microservices like the <a href=\"https:\/\/kindsonthegenius.com\/blog\/inventoryms-complete-application-spring-boot-api\/\">InventoryMS Spring Boot API<\/a>.<\/p>\n<h2 id=\"benchmark\">Step 5: Benchmark Platform vs Virtual Threads<\/h2>\n<p>Load-test with <code>hey<\/code> (install via Homebrew or GitHub releases):<\/p>\n<pre><code class=\"language-bash\"># With virtual threads enabled (default after step 2)\nhey -n 5000 -c 200 http:\/\/localhost:8080\/api\/products\/1\n\n# Disable virtual threads, restart, and compare:\n# spring.threads.virtual.enabled=false\nhey -n 5000 -c 200 http:\/\/localhost:8080\/api\/products\/1\n<\/code><\/pre>\n<figure style=\"margin: 1.5em 0; text-align: center;\"><img loading=\"lazy\" decoding=\"async\" style=\"max-width: 100%; height: auto; border-radius: 8px; border: 1px solid #dee2e6;\" src=\"https:\/\/kindsonthegenius.com\/blog\/wp-content\/uploads\/2026\/07\/spring-boot-virtual-threads-benchmark.png\" alt=\"hey load test results comparing requests per second with virtual threads enabled versus disabled\" width=\"640\" height=\"400\" \/>\n<figcaption style=\"font-size: 0.85em; color: #666; margin-top: 0.5em;\">Virtual threads typically sustain higher concurrency under I\/O-bound load<\/figcaption>\n<\/figure>\n<h2 id=\"faq\">Frequently Asked Questions<\/h2>\n<p><strong>Do I need to change my Spring Boot controller code for virtual threads?<\/strong><br \/>No. Enable <code>spring.threads.virtual.enabled=true<\/code> and existing blocking MVC controllers run on virtual threads automatically. Avoid wrapping every call in <code>CompletableFuture<\/code> unless you have a specific reason.<\/p>\n<p><strong>Are virtual threads available in Java 17?<\/strong><br \/>Virtual threads were preview\/incubator in Java 19\u201320 and finalized in <strong>Java 21 LTS<\/strong>. Spring Boot 3.2+ requires Java 17 minimum but virtual thread support targets Java 21.<\/p>\n<p><strong>What is thread pinning and should I worry?<\/strong><br \/>Pinning occurs when synchronized blocks or native code prevent a virtual thread from unmounting. Most Spring\/JDBC drivers are pinning-safe in 2026, but profile CPU-bound synchronized code if throughput plateaus.<\/p>\n<p><strong>Do virtual threads work with Spring WebFlux?<\/strong><br \/>WebFlux is already reactive\/non-blocking. Virtual threads target the <strong>Servlet stack<\/strong> (Spring MVC + Tomcat). Use one model per service \u2014 don&#8217;t mix unnecessarily.<\/p>\n<p><strong>Can I use virtual threads with @Async?<\/strong><br \/>Yes. Define a <code>TaskExecutor<\/code> bean using <code>Executors.newVirtualThreadPerTaskExecutor()<\/code> and reference it in <code>@Async(\"virtualThreadExecutor\")<\/code>.<\/p>\n<p><strong>How does this relate to the RAG chatbot tutorial?<\/strong><br \/>AI chat endpoints are I\/O-bound (embedding + LLM calls). Virtual threads let a RAG service handle many concurrent users \u2014 see the <a href=\"https:\/\/kindsonthegenius.com\/blog\/how-to-build-a-rag-chatbot-with-spring-boot-and-openai-step-by-step\/\">Spring Boot RAG tutorial<\/a>.<\/p>\n<h2>Final Thoughts<\/h2>\n<p>Java 21 virtual threads make high-concurrency Spring Boot applications much easier to build without forcing developers to abandon the familiar servlet programming model. For applications that spend significant time waiting on databases, HTTP services, or other I\/O operations, they can provide a practical way to handle more concurrent requests with less thread-management overhead.<\/p>\n<p>The key is understanding where virtual threads help. They are designed for workloads that spend time waiting, not for making CPU-intensive operations faster. That is why benchmarking your actual application matters more than assuming virtual threads will automatically improve performance.<\/p>\n<p>Start with the simple Spring Boot configuration, test it against a realistic workload, and measure the results. From there, you can decide whether virtual threads are the right fit for your REST API and downstream services.<\/p>\n<h2 id=\"next-steps\">Next Steps<\/h2>\n<ul>\n<li><a href=\"https:\/\/kindsonthegenius.com\/blog\/how-to-build-a-rag-chatbot-with-spring-boot-and-openai-step-by-step\/\"><strong>Build a RAG Chatbot with Spring Boot<\/strong> \u2014 AI + REST API<\/a><\/li>\n<li><a href=\"https:\/\/kindsonthegenius.com\/blog\/deploy-spring-boot-kubernetes-docker-compose-minikube-2026\/\"><strong>Deploy Spring Boot to Kubernetes<\/strong> \u2014 production deployment<\/a><\/li>\n<li><a href=\"https:\/\/kindsonthegenius.com\/blog\/json-web-token-how-to-secure-rest-api-using-jwt-in-spring-boot\/\">Secure REST APIs with JWT in Spring Boot<\/a><\/li>\n<li><a href=\"https:\/\/kindsonthegenius.com\/blog\/15-easy-free-java-tutorials-with-step-by-step-examples-and-quiz\/\">15 Easy Free Java Tutorials<\/a><\/li>\n<li><a href=\"https:\/\/www.alkademy.com\/courses\" target=\"_blank\" rel=\"noopener noreferrer\">Alkademy Java courses<\/a><\/li>\n<\/ul>\n<p><!-- ktg-series-nav --><\/p>\n<nav class=\"ktg-series-nav\" aria-label=\"Spring Boot 2026 tutorial series\">\n<p><strong>Series:<\/strong> <a href=\"https:\/\/kindsonthegenius.com\/blog\/how-to-build-a-rag-chatbot-with-spring-boot-and-openai-step-by-step\/\">RAG Chatbot<\/a> \u00b7 <a href=\"https:\/\/kindsonthegenius.com\/blog\/deploy-spring-boot-kubernetes-docker-compose-minikube-2026\/\">Kubernetes Deployment<\/a><\/p>\n<\/nav>\n<p><!-- ktg-alkademy-cta --><\/p>\n<div class=\"ktg-alkademy-cta\" style=\"margin: 2em 0; padding: 1.25em; border-left: 4px solid #2563eb; background: #f8fafc;\">\n<p><strong>Master modern Java?<\/strong> Join <a href=\"https:\/\/www.alkademy.com\/courses\" target=\"_blank\" rel=\"noopener noreferrer\">Alkademy<\/a> for live Java 21 and Spring Boot courses with real-world projects.<\/p>\n<\/div>\n<p><!-- ktg-faq-schema --><br \/><script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@type\":\"FAQPage\",\"mainEntity\":[{\"@type\":\"Question\",\"name\":\"Do I need to change Spring Boot controller code for virtual threads?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"No. Set spring.threads.virtual.enabled=true and existing blocking MVC controllers run on virtual threads automatically in Spring Boot 3.2+.\"}},{\"@type\":\"Question\",\"name\":\"Are virtual threads available in Java 17?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Virtual threads were finalized in Java 21 LTS. Spring Boot 3.2+ supports them when running on JDK 21.\"}},{\"@type\":\"Question\",\"name\":\"Do virtual threads work with Spring WebFlux?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"WebFlux is reactive and non-blocking. Virtual threads target the Servlet stack (Spring MVC + Tomcat). Use one model per service.\"}},{\"@type\":\"Question\",\"name\":\"When should I not use virtual threads?\",\"acceptedAnswer\":{\"@type\":\"Answer\",\"text\":\"Avoid virtual threads for CPU-bound workloads, heavy synchronized hot paths, or legacy libraries that pin carrier threads.\"}}]}<\/script><\/p>\n<p><!-- ktg-howto-schema --><br \/><script type=\"application\/ld+json\">{\"@context\":\"https:\/\/schema.org\",\"@type\":\"HowTo\",\"name\":\"Spring Boot 3 with Java 21 Virtual Threads REST API\",\"description\":\"Enable Java 21 virtual threads in Spring Boot 3 and build a high-throughput blocking REST API.\",\"totalTime\":\"PT75M\",\"tool\":[{\"@type\":\"HowToTool\",\"name\":\"JDK 21\"},{\"@type\":\"HowToTool\",\"name\":\"Spring Boot 3.3\"},{\"@type\":\"HowToTool\",\"name\":\"Maven\"}],\"step\":[{\"@type\":\"HowToStep\",\"name\":\"Create Spring Boot project\",\"text\":\"Generate a Spring Boot 3.3 project with Java 21 and Spring Web dependency.\"},{\"@type\":\"HowToStep\",\"name\":\"Enable virtual threads\",\"text\":\"Add spring.threads.virtual.enabled=true to application.properties.\"},{\"@type\":\"HowToStep\",\"name\":\"Create REST controller\",\"text\":\"Build a controller with blocking I\/O simulation using Thread.sleep or JDBC calls.\"},{\"@type\":\"HowToStep\",\"name\":\"Test endpoints\",\"text\":\"Run the application and verify JSON responses with curl.\"},{\"@type\":\"HowToStep\",\"name\":\"Benchmark concurrency\",\"text\":\"Load test with hey or JMeter comparing virtual threads enabled vs disabled.\"}]}<\/script><\/p>","protected":false},"excerpt":{"rendered":"<p>Learn how to use Java 21 virtual threads with Spring Boot 3 to build REST APIs that handle thousands of concurrent I\/O-bound requests without thread-pool &hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"pagelayer_contact_templates":[],"_pagelayer_content":"","footnotes":""},"categories":[85],"tags":[],"class_list":["post-2214","post","type-post","status-publish","format-standard","hentry","category-java"],"acf":[],"_links":{"self":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2214","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/comments?post=2214"}],"version-history":[{"count":1,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2214\/revisions"}],"predecessor-version":[{"id":2500,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/posts\/2214\/revisions\/2500"}],"wp:attachment":[{"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/media?parent=2214"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/categories?post=2214"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/kindsonthegenius.com\/blog\/wp-json\/wp\/v2\/tags?post=2214"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}