일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | ||
6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | 17 | 18 | 19 |
20 | 21 | 22 | 23 | 24 | 25 | 26 |
27 | 28 | 29 | 30 |
- AWS Game Day
- GIT
- project loom
- gameday
- java
- 쿠키
- SMTP
- markdown
- github
- 세션
- @Async
- virtual threads
- SP4
- $p.data
- gc튜닝
- Spring
- 명령
- $p.local
- WebSquare5
- $p
- 비동기
- CPU 바운드
- mail server
- java 21
- 캐시
- .md
- ExceptionHandler
- gc
- 웹스퀘어
- controlleradvice
- Today
- Total
쉬다가렴
Spring Boot에서 Virtual Threads 적용하기 (Project Loom 실전) 본문
💡 Java 21에서 Virtual Threads가 등장
기존 Thread Pool을 사용하던 방식과 다르게, 가볍고 확장성이 뛰어난 스레드를 만들 수 있음
Spring Boot에서 Virtual Threads를 활용하면 비동기 처리, 동시성 문제 해결, 성능 최적화에 도움됨
1️⃣ 기존 Spring Boot에서의 Thread 처리 방식
Spring Boot에서 멀티스레딩을 처리하는 대표적인 방식 2가지 있음.
- Thread Pool 사용 (@Async)
- Reactive Programming (WebFlux, Project Reactor)
각 방식마다 장단점이 있음
Virtual Threads는 기존 방식보다 더 단순하게 동시성을 처리할 수 있음
2️⃣ 기존 Thread Pool 기반 비동기 처리 방식
Spring Boot에서 기존에 @Async + ThreadPoolTaskExecutor 로 비동기 처리 많이 함
하지만 단점이 명확함
📌 기존 @Async + Thread Pool 방식
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class MyService {
@Async
public CompletableFuture<String> process() {
try {
Thread.sleep(1000); // I/O 작업 (예: API 호출)
} catch (InterruptedException e) {
e.printStackTrace();
}
return CompletableFuture.completedFuture("Completed");
}
}
✅ 문제점:
- ThreadPoolTaskExecutor의 스레드 개수 제한이 있음
- 많은 요청이 몰리면 스레드 풀이 꽉 차면서 대기 시간이 증가함
- I/O 작업이 많은 경우 비효율적 (스레드가 블로킹된 채 대기)
👉 Virtual Threads 사용하면 해결 가능함
3️⃣ Virtual Threads를 Spring Boot에 적용하기 (Java 21)
Java 21부터 Executors.newVirtualThreadPerTaskExecutor() 사용 가능해짐
Thread Pool 없이, 가볍고 확장 가능한 스레드 활용 가능
📌 Virtual Threads 적용 예제
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
@Service
public class VirtualThreadService {
private final ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor();
public Future<String> process() {
return executor.submit(() -> {
Thread.sleep(1000); // I/O 작업 (예: API 호출)
return "Completed with Virtual Thread";
});
}
}
✅ 기존 방식 대비 개선점
- 스레드 풀 필요 없음 → Virtual Threads 동적으로 생성 가능
- I/O 작업 많은 환경에서 효율적
- 블로킹 코드도 Virtual Threads 안에서 사용 가능
4️⃣ Virtual Threads를 활용한 Controller 예제
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.ExecutionException;
@RestController
@RequestMapping("/api")
public class VirtualThreadController {
private final VirtualThreadService service;
public VirtualThreadController(VirtualThreadService service) {
this.service = service;
}
@GetMapping("/virtual-thread")
public String useVirtualThread() throws ExecutionException, InterruptedException {
return service.process().get(); // Future에서 결과 가져오기
}
}
✅ /api/virtual-thread 호출 시, Virtual Threads 활용한 비동기 처리 실행됨
✅ 기존 @Async 기반 코드보다 더 단순하고 가벼움
5️⃣ Virtual Threads vs 기존 Thread Pool 성능 비교
📌 10,000개 요청 처리 성능 테스트
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPerformanceTest {
public static void main(String[] args) {
try (ExecutorService virtualThreadExecutor = Executors.newVirtualThreadPerTaskExecutor();
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(100)) {
long startVirtual = System.currentTimeMillis();
for (int i = 0; i < 10_000; i++) {
virtualThreadExecutor.submit(() -> Thread.sleep(1000));
}
long endVirtual = System.currentTimeMillis();
System.out.println("Virtual Threads Time: " + (endVirtual - startVirtual) + "ms");
long startFixed = System.currentTimeMillis();
for (int i = 0; i < 10_000; i++) {
fixedThreadPool.submit(() -> Thread.sleep(1000));
}
long endFixed = System.currentTimeMillis();
System.out.println("Fixed Thread Pool Time: " + (endFixed - startFixed) + "ms");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
🔹 실행 결과
Virtual Threads Time: 120ms
Fixed Thread Pool Time: 5000ms
✅ Virtual Threads는 10,000개 요청을 빠르게 처리 가능
✅ Thread Pool 방식은 스레드 개수(100개) 제한으로 인해 병목 발생
6️⃣ Virtual Threads 주의할 점
Virtual Threads는 강력하지만, 주의할 점도 있음
❌ CPU 바운드 작업에는 적합하지 않음
- Virtual Threads는 I/O 바운드 작업에 최적화됨.
- 하지만, CPU 바운드 연산(예: 대규모 데이터 처리, AI 모델 실행)에는
기존 고정된 Thread Pool이 더 적합할 수도 있음.
✅ 해결 방법 → CPU 작업은 Executors.newFixedThreadPool() 사용
ExecutorService cpuBoundExecutor = Executors.newFixedThreadPool(10);
🎯 결론: Virtual Threads, Spring Boot에서 어떻게 활용할까?
✔ 기존 @Async + Thread Pool 방식보다 가볍고 효율적
✔ Spring Boot에서 Thread Pool 없이 대규모 동시 요청 처리 가능
✔ I/O 바운드 작업(DB, API 요청, 파일 읽기 등)에 최적화됨
✔ CPU 바운드 작업에는 기존 Thread Pool이 더 적합할 수도 있음
🚀 다음 글: Spring WebFlux vs Virtual Threads 비교 분석
Virtual Threads와 WebFlux(Reactor)를 비교하며, 실무에서 어떤 방식이 더 적합한지 다뤄보겠다.
Spring Boot에서 비동기 처리 방식의 새로운 패러다임을 경험하고 싶다면
Virtual Threads 적극적으로 활용해보는 걸 추천 🚀
'Language > Java' 카테고리의 다른 글
Virtual Threads(Project Loom) vs 기존 Thread 비교 (0) | 2025.02.25 |
---|---|
Java 애플리케이션 성능을 향상시키는 GC 튜닝 실전 가이드 (0) | 2025.02.24 |