Language/Java

Virtual Threads(Project Loom) vs 기존 Thread 비교

예스맨 2025. 2. 25. 08:21

왜 Virtual Threads가 필요한가 ...?!

1️⃣ 기존 Thread의 문제점

Java의 기존 Thread는 OS 커널 스레드를 기반으로 실행됩니다.
즉, 하나의 Java Thread는 OS의 물리적인 스레드와 1:1로 매핑되며, 다음과 같은 문제가 있습니다.

🔹 기존 Thread의 문제점

  1. 비싼 생성 비용
    • new Thread()를 생성하면 OS에서 새로운 커널 스레드를 할당해야 하므로 비용이 큼
    • 일반적으로 하나의 Thread는 약 1MB의 스택 메모리를 차지
  2. 제한된 동시성
    • Thread 개수가 많아지면OS가 스레드 스케줄링을 관리하는 데 부담이 커짐
    • 수천 개 이상의 동시 요청을 처리하는 서버에서는 스레드 풀(Thread Pool)을 사용해야 함
  3. I/O 작업 시 비효율적
    • 스레드가 I/O 작업(예: DB 조회, API 호출)을 수행하면, 해당 스레드는 아무것도 하지 않고 대기.
    • 이로 인해 CPU 활용도가 낮아지고 성능이 저하됨.

📌 기존 Thread의 한계 실험 (코드 예제)

아래 코드는 10,000개의 Thread를 생성하는 경우 어떻게 동작하는지 보여줍니다.

public class TraditionalThreadTest {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10_000; i++) {
            Thread thread = new Thread(() -> {
                try {
                    Thread.sleep(1000); // 1초 대기 (I/O 작업 시뮬레이션)
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
            thread.start();
        }
    }
}

 

💥 결과

  • 실행 시 메모리 부족(OutOfMemoryError) 발생 가능
  • Thread 생성 비용이 크기 때문에 10,000개 이상의 스레드를 만들면 OS가 감당 못 함! 😵

2️⃣ Virtual Threads (Project Loom) 등장 🚀

Java 21부터 도입된 Virtual ThreadsOS 커널 스레드와 독립적으로 동작하는 가벼운 스레드입니다.
💡 즉, 수천~수백만 개의 동시 실행을 쉽게 처리할 수 있도록 해줍니다.

🔹 Virtual Threads의 특징

  1. 가볍다!
    • Virtual Thread는 커널 스레드를 사용하지 않고, Java의 스케줄러가 관리
    • 생성 비용이 거의 없고, 메모리 사용량이 적음 (스택 크기 ≈ 4KB)
  2. 엄청난 동시성 처리 가능
    • 기존 Thread는 CPU 코어 수만큼 효율적으로 사용할 수 있지만 Virtual Thread는 수백만 개도 가능
    • I/O 대기 시간이 많은 애플리케이션에서 성능이 극적으로 향상됨
  3. 스레드 풀이 필요 없음
    • 기존에는 성능을 위해 ThreadPoolExecutor를 사용했지만, Virtual Threads는 필요한 만큼 바로 생성해서 실행하면 됨

3️⃣ 기존 Thread vs Virtual Threads 비교 (코드 예제)

🔹 기존 Thread 방식

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class TraditionalThreadPoolExample {
    public static void main(String[] args) {
        ExecutorService executor = Executors.newFixedThreadPool(100); // 스레드 풀 크기: 100

        for (int i = 0; i < 10_000; i++) {
            executor.submit(() -> {
                try {
                    Thread.sleep(1000); // I/O 작업 시뮬레이션
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
    }
}

 

👀 제한 사항:

  • 스레드 풀 크기가 100개로 제한되었기 때문에 10,000개의 작업이 병렬로 처리되지 않음
  • 더 많은 동시 작업을 처리하려면 스레드 풀 크기를 증가시켜야 하는데,
    메모리 사용량이 급격히 증가할 수 있음

🔹 Virtual Thread 방식 (Java 21)

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class VirtualThreadExample {
    public static void main(String[] args) {
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < 10_000; i++) {
                executor.submit(() -> {
                    try {
                        Thread.sleep(1000); // I/O 작업 시뮬레이션
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
    }
}

 

 

🎉 결과:
✅ 10,000개 이상의 Virtual Threads를 문제없이 실행 가능!
✅ 스레드 풀이 필요 없음 → OS 부담이 적고, 동시성 처리가 대폭 향상됨.


4️⃣ 실무 적용 사례

📌 Virtual Threads를 사용하면 좋은 경우

  1. I/O 바운드 애플리케이션 (DB 조회, API 호출이 많은 경우)
    • ex: 웹 크롤러, HTTP API 서버, 대규모 데이터 처리
    • 기존 Thread는 I/O 대기 시간 동안 비효율적 → Virtual Threads로 변경하면 훨씬 가볍고 빠르게 실행 가능
  2. 대규모 동시 요청 처리 (웹 서버, 마이크로서비스)
    • 기존 ThreadPool을 사용하면 제한된 크기만큼만 동시 요청을 처리할 수 있음.
    • Virtual Threads는 무제한 생성 가능 → 더 많은 요청을 처리할 수 있음
  3. 비동기 코드 간소화
    • 기존에는 CompletableFuture 또는 **Reactor(웹플럭스)**로 비동기 프로그래밍을 했지만,
      Virtual Threads를 사용하면 동기 방식 코드 그대로 성능을 높일 수 있음

5️⃣ 기존 Thread vs Virtual Threads 비교 정리

기존 ThreadVirtual Thread (Project Loom)

스레드 생성 비용 높음 (OS 커널 스레드 필요) 매우 낮음 (가벼운 사용자 스레드)
메모리 사용량 1MB/Thread 약 4KB/Thread
I/O 대기 효율성 낮음 (스레드가 대기 상태) 높음 (다른 작업 수행 가능)
적합한 애플리케이션 CPU 바운드 작업 I/O 바운드 작업
최대 동시 실행 수 제한적 (OS 스레드 개수 영향) 수백만 개 가능

 


🎯 결론: Virtual Threads, 언제 도입해야 할까?

✔ 기존 Thread는 CPU 바운드 작업에서는 여전히 강력하지만,
I/O 바운드 애플리케이션에서는 Virtual Threads가 성능을 극적으로 향상시킬 수 있음
✔ Virtual Threads를 활용하면 스레드 풀 없이 동시성 처리를 단순화할 수 있음

💡 🚀 Java 21 이후의 새로운 트렌드!
Virtual Threads를 활용하면 비동기 프로그래밍 없이도 강력한 동시성을 제공할 수 있다.
앞으로 Spring Boot와 같은 프레임워크에서도 Virtual Threads 지원이 확장될 것으로 보인다.

👉 다음 글에서는 Virtual Threads를 Spring Boot에서 어떻게 적용하는지 실전 예제를 다루겠습니다! 🚀