<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Virtual Threads on jyukki's Blog</title><link>https://jyukki.com/tags/virtual-threads/</link><description>Recent content in Virtual Threads on jyukki's Blog</description><generator>Hugo -- 0.147.0</generator><language>ko-kr</language><lastBuildDate>Sat, 27 Jun 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://jyukki.com/tags/virtual-threads/index.xml" rel="self" type="application/rss+xml"/><item><title>백엔드 커리큘럼 심화: Java Virtual Threads, Spring MVC와 WebFlux 사이에서 고르는 실무 기준</title><link>https://jyukki.com/learning/deep-dive/deep-dive-java-virtual-threads-spring-mvc-webflux-playbook/</link><pubDate>Sat, 27 Jun 2026 00:00:00 +0000</pubDate><guid>https://jyukki.com/learning/deep-dive/deep-dive-java-virtual-threads-spring-mvc-webflux-playbook/</guid><description>Java Virtual Threads를 단순 성능 옵션이 아니라 요청 처리 모델, DB/HTTP 풀, WebFlux 선택 기준까지 함께 보는 실무 플레이북으로 정리합니다.</description><content:encoded><![CDATA[<p>Java 21 이후 백엔드 팀에서 Virtual Threads는 더 이상 실험 기능이 아닙니다. Spring Boot에서도 설정 한 줄로 요청 처리나 비동기 실행에 가상 스레드를 붙일 수 있습니다. 그래서 종종 &ldquo;이제 WebFlux를 안 써도 되나요?&rdquo;, &ldquo;스레드 풀 튜닝은 끝난 건가요?&rdquo;, &ldquo;블로킹 JDBC도 그냥 많이 띄우면 되는 건가요?&rdquo; 같은 질문이 나옵니다.</p>
<p>결론부터 말하면 Virtual Threads는 <strong>블로킹 I/O 중심 애플리케이션의 동시성 비용을 크게 낮추는 도구</strong>입니다. 하지만 DB 커넥션 풀, HTTP 클라이언트 풀, 외부 API quota, CPU 코어 수, timeout budget까지 같이 사라지는 것은 아닙니다. 스레드를 싸게 만들었더니 병목이 더 아래 계층으로 이동하는 경우가 많습니다.</p>
<p>이 글은 Virtual Threads를 &ldquo;켜면 빨라진다&rdquo; 수준으로 다루지 않습니다. <a href="/learning/deep-dive/deep-dive-thread-pool/">Thread Pool 튜닝</a>, <a href="/learning/deep-dive/deep-dive-spring-webflux-vs-mvc/">WebFlux vs MVC 선택 가이드</a>, <a href="/learning/deep-dive/deep-dive-connection-pool-sizing-saturation-playbook/">커넥션 풀 사이징</a>, <a href="/learning/deep-dive/deep-dive-timeout-retry-backoff/">Timeout/Retry/Backoff 설계</a>와 연결해서, 어떤 서비스에 먼저 켜고 어떤 서비스에서는 보류해야 하는지 실무 기준으로 정리합니다.</p>
<h2 id="이-글에서-얻는-것">이 글에서 얻는 것</h2>
<ul>
<li>Virtual Threads가 해결하는 문제와 해결하지 못하는 문제를 구분할 수 있습니다.</li>
<li>Spring MVC, WebFlux, <code>@Async</code>, scheduler에서 Virtual Threads를 어떤 기준으로 적용할지 판단할 수 있습니다.</li>
<li>DB/HTTP connection pool, admission control, timeout을 Virtual Threads와 함께 조정하는 기준을 잡을 수 있습니다.</li>
<li>도입 전후에 반드시 봐야 하는 지표와 canary 조건을 숫자로 정리할 수 있습니다.</li>
<li>면접이나 설계 리뷰에서 &ldquo;Virtual Threads를 쓰면 WebFlux가 필요 없나&quot;라는 질문에 현실적인 답변을 만들 수 있습니다.</li>
</ul>
<h2 id="핵심-개념이슈">핵심 개념/이슈</h2>
<h3 id="1-virtual-threads는-os-스레드가-아니라-jvm이-관리하는-가벼운-작업-단위다">1) Virtual Threads는 OS 스레드가 아니라 JVM이 관리하는 가벼운 작업 단위다</h3>
<p>기존 Java 서버는 요청 하나를 platform thread 하나에 매핑하는 구조가 흔했습니다. platform thread는 OS 스레드와 연결되므로 생성 비용과 메모리 비용이 큽니다. 그래서 Tomcat worker thread, <code>@Async</code> executor, scheduler thread pool을 제한적으로 운영했고, 큐가 길어지면 thread pool saturation이 곧 지연 증가로 이어졌습니다.</p>
<p>Virtual Threads는 이 비용 구조를 바꿉니다. 요청이 네트워크 I/O, DB I/O, 파일 I/O처럼 기다리는 동안 JVM은 underlying carrier thread를 다른 virtual thread 실행에 쓸 수 있습니다. 즉 &ldquo;기다리는 작업&quot;을 많이 다루는 서비스에서 thread per request 모델의 단순함을 유지하면서 더 많은 동시성을 감당할 수 있습니다.</p>
<p>하지만 중요한 전제는 <strong>기다리는 시간이 많은 workload</strong>입니다. CPU를 오래 태우는 암호화, 이미지 변환, 대형 JSON 직렬화, 압축, 복잡한 계산은 Virtual Threads로 빨라지지 않습니다. CPU-bound 작업은 결국 코어 수가 상한입니다. 이 경우는 <a href="/learning/deep-dive/deep-dive-os-concurrency-basics/">OS 동시성 기초</a>와 <a href="/learning/deep-dive/deep-dive-thread-pool/">Thread Pool 튜닝</a> 기준으로 별도 executor를 두는 편이 맞습니다.</p>
<h3 id="2-스레드-병목이-줄면-db와-외부-api-병목이-더-빨리-드러난다">2) 스레드 병목이 줄면 DB와 외부 API 병목이 더 빨리 드러난다</h3>
<p>Virtual Threads를 켠 뒤 가장 흔한 착각은 &ldquo;이제 동시 요청을 훨씬 많이 받을 수 있다&quot;입니다. 애플리케이션 스레드만 보면 맞습니다. 하지만 요청 하나가 DB 커넥션 1개와 외부 API connection 1개를 잡는다면 전체 처리량은 여전히 하류 자원에 묶입니다.</p>
<p>예를 들어 기존에는 Tomcat worker 200개가 상한이어서 DB 풀 30개가 크게 문제 되지 않았다고 해 봅시다. Virtual Threads를 켜면 요청 진입 자체는 훨씬 넓어질 수 있습니다. 그런데 DB 풀은 그대로 30개이고, 외부 결제 API가 초당 100건만 허용한다면, 시스템은 더 많은 요청을 안쪽으로 받아들인 뒤 더 많이 기다리게 만들 수 있습니다. 이때 p99는 좋아지기는커녕 나빠질 수 있습니다.</p>
<p>그래서 Virtual Threads 도입은 thread pool 확대가 아니라 <strong>동시성 병목 위치를 다시 그리는 작업</strong>입니다. 봐야 할 질문은 아래입니다.</p>
<ul>
<li>DB 커넥션 풀 wait p95가 20ms 이하로 유지되는가?</li>
<li>외부 API connection pool pending이 피크 5분 동안 계속 쌓이지 않는가?</li>
<li>retry rate가 기존 대비 1%p 이상 늘지 않는가?</li>
<li>API timeout 안에서 DB wait, HTTP wait, business logic 시간이 모두 닫히는가?</li>
<li>admission control 없이 모든 요청을 계속 받아도 되는 경로인가?</li>
</ul>
<p>이 질문에 답하지 못하면 Virtual Threads는 &ldquo;빠른 서버&quot;가 아니라 &ldquo;더 많은 대기열&quot;을 만들 수 있습니다.</p>
<h3 id="3-spring-boot에서는-켜기-쉽지만-적용-범위-확인이-더-중요하다">3) Spring Boot에서는 켜기 쉽지만, 적용 범위 확인이 더 중요하다</h3>
<p>Spring Boot는 Java 21 이상에서 <code>spring.threads.virtual.enabled=true</code>를 설정하면 기본 task executor와 scheduler에 virtual thread 기반 실행을 적용할 수 있습니다. Servlet stack에서도 virtual thread 기반 request handling을 적용할 수 있습니다. 설정 자체는 쉽습니다.</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-properties" data-lang="properties"><span style="display:flex;"><span><span style="color:#50fa7b">spring.threads.virtual.enabled</span><span style="color:#ff79c6">=</span><span style="color:#f1fa8c">true</span>
</span></span></code></pre></div><p>문제는 설정이 쉬운 만큼 &ldquo;어디에 적용됐는지&quot;를 놓치기 쉽다는 점입니다. MVC controller는 virtual thread로 실행되지만, 내부에서 호출하는 별도 executor, Kafka listener, batch worker, scheduled job, custom <code>CompletableFuture</code> executor는 기존 platform thread pool을 계속 쓸 수 있습니다. 반대로 모든 비동기 작업이 virtual thread로 바뀌면, 기존에 thread pool size로 보호하던 하류 자원 제한이 사라질 수 있습니다.</p>
<p>실무에서는 아래 범위를 분리해야 합니다.</p>
<ul>
<li>HTTP request handling: MVC controller와 filter/interceptor 경로</li>
<li>비동기 작업: <code>@Async</code>, <code>CompletableFuture</code>, application event listener</li>
<li>scheduler: <code>@Scheduled</code>, 배치 트리거, 주기적 sync</li>
<li>메시지 소비: Kafka/RabbitMQ listener concurrency</li>
<li>외부 호출: WebClient, RestClient, JDBC, Redis, object storage SDK</li>
</ul>
<p>Virtual Threads는 &ldquo;모든 executor를 없애자&quot;가 아니라, <strong>요청 실행 모델을 단순화하되 자원 보호용 limit은 별도로 유지하자</strong>에 가깝습니다.</p>
<h3 id="4-pinning-threadlocal-synchronized는-도입-전-반드시-점검해야-한다">4) pinning, ThreadLocal, synchronized는 도입 전 반드시 점검해야 한다</h3>
<p>Virtual Threads는 대부분의 일반적인 blocking I/O에서 잘 작동하지만, 특정 상황에서는 carrier thread를 붙잡을 수 있습니다. 대표적으로 오래 걸리는 <code>synchronized</code> 블록, native call, 일부 오래된 라이브러리의 blocking 구간, 과도한 <code>ThreadLocal</code> 사용이 문제가 됩니다.</p>
<p>특히 Spring/JPA 기반 애플리케이션은 <code>ThreadLocal</code>을 자주 씁니다. transaction context, security context, MDC logging context, request context가 thread 단위로 붙습니다. Virtual Threads는 많아질 수 있으므로, thread-local에 큰 객체를 넣거나 요청 종료 후 정리하지 않으면 메모리 사용량과 진단 난이도가 올라갑니다.</p>
<p>점검 기준은 단순합니다.</p>
<ul>
<li>request당 ThreadLocal payload가 큰 객체를 들고 있지 않은가?</li>
<li>synchronized 구간이 외부 I/O나 DB 호출을 감싸고 있지 않은가?</li>
<li>legacy JDBC/HTTP client에서 pinning 경고가 발생하지 않는가?</li>
<li>로그 MDC가 요청 종료 후 확실히 정리되는가?</li>
<li>virtual thread dump를 봤을 때 특정 lock에서 오래 멈춘 작업이 없는가?</li>
</ul>
<p>pinning이 조금 있다고 바로 실패는 아닙니다. 하지만 hot path에서 반복되면 Virtual Threads의 장점이 급격히 줄어듭니다.</p>
<h3 id="5-webflux가-사라지는-것이-아니라-선택-기준이-바뀐다">5) WebFlux가 사라지는 것이 아니라 선택 기준이 바뀐다</h3>
<p>Virtual Threads가 나오면서 &ldquo;WebFlux는 이제 필요 없나&quot;라는 질문이 자주 나옵니다. 제 답은 &ldquo;많은 CRUD/API 서버에서는 MVC + Virtual Threads가 더 단순한 선택이 될 수 있지만, WebFlux의 영역은 여전히 남는다&quot;입니다.</p>
<p>MVC + Virtual Threads가 유리한 경우:</p>
<ul>
<li>기존 코드가 blocking MVC/JDBC 중심이다.</li>
<li>팀이 reactive operator와 backpressure 디버깅에 익숙하지 않다.</li>
<li>요청 대부분이 DB/HTTP I/O 대기이며, CPU-bound 작업이 적다.</li>
<li>streaming보다 일반 request/response가 핵심이다.</li>
<li>트랜잭션, security context, 예외 처리 모델을 단순하게 유지하고 싶다.</li>
</ul>
<p>WebFlux가 여전히 유리한 경우:</p>
<ul>
<li>긴 streaming, SSE, websocket, 고밀도 connection 관리가 핵심이다.</li>
<li>reactive driver와 backpressure를 end-to-end로 설계했다.</li>
<li>요청 하나가 fan-out/fan-in, streaming transform, cancellation propagation을 많이 쓴다.</li>
<li>low-level event loop 튜닝과 operator 관측성을 팀이 운영할 수 있다.</li>
<li>thread-per-request 모델보다 non-blocking pipeline 자체가 제품 요구에 맞다.</li>
</ul>
<p>즉 Virtual Threads는 WebFlux를 대체한다기보다, &ldquo;단순한 blocking API 서버가 reactive로 넘어가야만 확장된다&quot;는 압박을 줄입니다. 선택 기준은 기술 유행이 아니라 workload와 팀 운영 능력입니다.</p>
<h2 id="실무-적용">실무 적용</h2>
<h3 id="1-도입-후보를-먼저-고른다">1) 도입 후보를 먼저 고른다</h3>
<p>Virtual Threads는 전사 default보다 후보 서비스 1~2개에 canary로 넣는 편이 안전합니다. 첫 후보는 아래 조건을 만족하는 서비스가 좋습니다.</p>
<ul>
<li>Java 21 이상, Spring Boot 3.2 이상으로 이미 운영 중이다.</li>
<li>request latency 중 DB/HTTP 대기가 50% 이상이다.</li>
<li>platform thread pool queue wait가 피크 시간에 반복적으로 관측된다.</li>
<li>CPU 사용률은 피크에도 60~70% 이하로 여유가 있다.</li>
<li>주요 외부 의존성의 timeout, connection pool, retry 정책이 이미 숫자로 정의돼 있다.</li>
<li>롤백이 설정 변경 또는 단일 배포로 가능하다.</li>
</ul>
<p>반대로 아래 조건이면 보류가 낫습니다.</p>
<ul>
<li>CPU-bound 작업이 요청 시간의 대부분이다.</li>
<li>DB pool wait나 lock wait가 이미 높다.</li>
<li>외부 API 429/timeout이 자주 발생한다.</li>
<li>synchronized 기반 legacy 라이브러리가 hot path에 많다.</li>
<li>관측 지표가 thread, pool, DB wait를 분리하지 못한다.</li>
</ul>
<p>핵심은 &ldquo;Virtual Threads가 도움이 될 workload&quot;와 &ldquo;Virtual Threads가 병목을 가릴 workload&quot;를 나누는 것입니다.</p>
<h3 id="2-canary-기준은-평균-latency가-아니라-p95p99와-하류-포화로-본다">2) canary 기준은 평균 latency가 아니라 p95/p99와 하류 포화로 본다</h3>
<p>도입 검증은 최소 1주일 baseline을 잡고 시작하는 편이 좋습니다. 평일 피크, 배치 시간, 배포 직후를 포함해야 합니다. canary는 5~10% 트래픽부터 시작하고, 이상 없을 때 25%, 50%, 100%로 올립니다.</p>
<p>권장 gate는 아래처럼 잡을 수 있습니다.</p>
<table>
  <thead>
      <tr>
          <th>지표</th>
          <th>통과 기준</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>API latency p95</td>
          <td>baseline 대비 +10% 이내</td>
      </tr>
      <tr>
          <td>API latency p99</td>
          <td>baseline 대비 +15% 이내</td>
      </tr>
      <tr>
          <td>error/timeout rate</td>
          <td>baseline 대비 +0.5%p 이내</td>
      </tr>
      <tr>
          <td>DB pool acquire p95</td>
          <td>20ms 이하 또는 baseline 대비 악화 없음</td>
      </tr>
      <tr>
          <td>HTTP client pending</td>
          <td>5분 이상 지속 pending 없음</td>
      </tr>
      <tr>
          <td>retry rate</td>
          <td>baseline 대비 +1%p 이내</td>
      </tr>
      <tr>
          <td>CPU 사용률</td>
          <td>피크 75% 이상 지속 시 보류</td>
      </tr>
      <tr>
          <td>heap/GC</td>
          <td>request당 ThreadLocal 증가, GC pause 악화 없음</td>
      </tr>
  </tbody>
</table>
<p>Virtual Threads가 성공하면 평균 latency보다 queue wait와 thread starvation이 먼저 좋아질 가능성이 큽니다. 반대로 p99가 나빠졌다면 하류 pool, retry, lock wait, pinning을 봐야 합니다.</p>
<h3 id="3-하류-자원-limit은-더-엄격하게-둔다">3) 하류 자원 limit은 더 엄격하게 둔다</h3>
<p>Virtual Threads를 켠 뒤에도 DB pool을 무작정 키우면 안 됩니다. 오히려 처음에는 기존 pool을 유지하고, pool wait와 DB active session을 보면서 조정합니다.</p>
<p>추천 우선순위는 아래입니다.</p>
<ol>
<li>기존 DB/HTTP pool 크기는 유지한다.</li>
<li>API deadline, DB query timeout, HTTP client timeout을 먼저 정렬한다.</li>
<li>route별 또는 기능별 concurrency limit을 붙인다.</li>
<li>Virtual Threads canary 후 pool wait가 높고 하류 여유가 확인될 때만 10~20% 단위로 조정한다.</li>
<li>retry는 최대 2회 이하, 전체 request deadline의 20~25% 이내로 제한한다.</li>
</ol>
<p>예를 들어 주문 API deadline이 1초라면, DB pool wait 150ms, DB query 400ms, 외부 API 300ms, 나머지 application overhead 150ms처럼 예산을 나눠야 합니다. Virtual Threads가 있어도 deadline budget을 넘긴 작업은 취소되어야 합니다. 이 부분은 <a href="/learning/deep-dive/deep-dive-end-to-end-deadline-cancellation-playbook/">종단간 Deadline Budget과 Cancellation</a> 기준과 같이 적용합니다.</p>
<h3 id="4-운영-지표를-request-관점으로-다시-묶는다">4) 운영 지표를 request 관점으로 다시 묶는다</h3>
<p>Virtual Threads 도입 후 대시보드는 thread count보다 request lifecycle을 보여줘야 합니다.</p>
<p>필수 지표:</p>
<ul>
<li><code>http.server.requests</code> p50/p95/p99</li>
<li><code>db.pool.acquire</code> p95/p99, timeout count</li>
<li>DB active session, lock wait, slow query count</li>
<li>HTTP client pool pending, connect timeout, read timeout</li>
<li>virtual thread count, pinned event, thread dump 샘플</li>
<li><code>ThreadLocal</code> 관련 메모리 증가 의심 지표</li>
<li>route별 inflight requests와 admission reject</li>
</ul>
<p>특히 &ldquo;스레드는 여유가 있는데 DB가 터진다&quot;는 상황이 늘 수 있으므로, dashboard는 application thread만 보면 안 됩니다. request가 하류 자원을 얼마나 오래 잡는지 보여줘야 합니다.</p>
<h2 id="트레이드오프주의점">트레이드오프/주의점</h2>
<p>첫째, Virtual Threads는 blocking code를 더 싸게 운영하게 해 주지만, blocking dependency를 무한히 안전하게 만들어 주지는 않습니다. DB, Redis, 외부 API는 여전히 동시성 상한이 필요합니다.</p>
<p>둘째, MVC 코드의 단순함은 큰 장점입니다. reactive operator 체인을 줄이고 stack trace와 debugging을 단순하게 만들 수 있습니다. 하지만 streaming, backpressure, event pipeline을 이미 잘 설계한 팀이라면 WebFlux를 버릴 이유도 없습니다.</p>
<p>셋째, 기존 thread pool size가 하류 보호 장치 역할을 하고 있었다면 Virtual Threads 전환 후 보호막이 사라질 수 있습니다. 이 경우 admission control, bulkhead, route별 limit을 별도로 둬야 합니다.</p>
<p>넷째, pinning과 ThreadLocal은 &ldquo;나중에 보면 되겠지&quot;로 넘기면 안 됩니다. 성능 문제가 아니라 진단 가능성 문제입니다. 특정 lock, logging context, transaction context가 hot path에서 carrier thread를 붙잡으면 운영자가 원인을 찾기 어렵습니다.</p>
<p>다섯째, Virtual Threads는 성능 튜닝이 아니라 아키텍처 선택입니다. &ldquo;켜기 전&quot;보다 &ldquo;켜고 나서 어떤 병목을 관측하고 제한할 것인가&quot;가 더 중요합니다.</p>
<h2 id="체크리스트-또는-연습">체크리스트 또는 연습</h2>
<h3 id="체크리스트">체크리스트</h3>
<ul>
<li><input disabled="" type="checkbox"> Java 21 이상과 Spring Boot virtual thread 지원 범위를 확인했다.</li>
<li><input disabled="" type="checkbox"> 후보 서비스의 latency 중 I/O wait 비중이 50% 이상인지 확인했다.</li>
<li><input disabled="" type="checkbox"> DB pool acquire p95/p99, HTTP client pending, retry rate가 baseline으로 잡혀 있다.</li>
<li><input disabled="" type="checkbox"> CPU-bound 작업은 별도 executor 또는 worker로 분리했다.</li>
<li><input disabled="" type="checkbox"> <code>ThreadLocal</code>, MDC, security context, transaction context 정리 여부를 점검했다.</li>
<li><input disabled="" type="checkbox"> synchronized hot path와 오래된 JDBC/HTTP client의 pinning 가능성을 점검했다.</li>
<li><input disabled="" type="checkbox"> Virtual Threads canary를 5~10%부터 시작하고 p95/p99 gate를 정의했다.</li>
<li><input disabled="" type="checkbox"> DB/HTTP pool은 기존 값을 유지한 뒤 하류 여유가 확인될 때만 10~20% 단위로 조정한다.</li>
<li><input disabled="" type="checkbox"> WebFlux 유지/전환 판단을 팀 숙련도, streaming 요구, backpressure 요구 기준으로 문서화했다.</li>
</ul>
<h3 id="연습">연습</h3>
<ol>
<li>현재 운영 중인 Spring MVC API 하나를 골라 request time을 <code>DB wait</code>, <code>DB execution</code>, <code>외부 HTTP wait</code>, <code>CPU work</code>, <code>serialization</code>으로 나눠 보세요. I/O wait가 절반을 넘으면 Virtual Threads 후보가 될 수 있습니다.</li>
<li><code>spring.threads.virtual.enabled=true</code>를 staging에 적용하고, 같은 부하에서 platform thread 방식과 <code>db.pool.acquire_p95</code>, <code>http.client.pending</code>, <code>api_p99</code>를 비교해 보세요. API 평균만 비교하면 중요한 병목을 놓칩니다.</li>
<li>WebFlux로 작성된 endpoint 하나를 골라 &ldquo;이 코드가 reactive여야 하는 이유&quot;를 세 가지로 적어 보세요. streaming, backpressure, event loop, cancellation 중 명확한 이유가 없다면 MVC + Virtual Threads가 더 단순할 수 있습니다.</li>
<li>팀의 공통 라이브러리 중 <code>synchronized</code>와 <code>ThreadLocal</code>을 많이 쓰는 부분을 찾아 hot path 여부를 표시해 보세요. Virtual Threads 도입 전 가장 먼저 봐야 할 위험 지도입니다.</li>
</ol>
<h2 id="관련-글">관련 글</h2>
<ul>
<li><a href="/learning/deep-dive/deep-dive-thread-pool/">Thread Pool 튜닝</a></li>
<li><a href="/learning/deep-dive/deep-dive-spring-webflux-vs-mvc/">WebFlux vs MVC 선택 가이드</a></li>
<li><a href="/learning/deep-dive/deep-dive-connection-pool-sizing-saturation-playbook/">커넥션 풀 사이징과 포화 해석</a></li>
<li><a href="/learning/deep-dive/deep-dive-timeout-retry-backoff/">Timeout/Retry/Backoff 설계</a></li>
<li><a href="/learning/deep-dive/deep-dive-admission-control-concurrency-limits/">Admission Control과 Concurrency Limit</a></li>
<li><a href="/learning/deep-dive/deep-dive-os-concurrency-basics/">OS 동시성 기초</a></li>
</ul>
<h2 id="참고-링크">참고 링크</h2>
<ul>
<li>OpenJDK JEP 444: Virtual Threads - <a href="https://openjdk.org/jeps/444">https://openjdk.org/jeps/444</a></li>
<li>Oracle Java 21 Docs: Virtual Threads - <a href="https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html">https://docs.oracle.com/en/java/javase/21/core/virtual-threads.html</a></li>
<li>Spring Boot Reference: Task Execution and Scheduling - <a href="https://docs.spring.io/spring-boot/reference/features/task-execution-and-scheduling.html">https://docs.spring.io/spring-boot/reference/features/task-execution-and-scheduling.html</a></li>
</ul>
]]></content:encoded></item></channel></rss>