개발/기술

Virtual Thread in JAVA

kwony 2026. 1. 14. 11:46

작업마다 Java Thread 를 생성하면 OS 단위에서 Thread 를 생성하기 때문에 비싸고 시간도 오래 걸린다. 그래서 Java 진영에선 기존에 생성하던 Java Thread 를 Platform Thread 라고 정의하고 Virtual Thread 라는 것을 새로 만들어서 Platform Thread 위에서 실행되는 구조를 잡았다. 

출처: 우아한기술블로그 (https://techblog.woowahan.com/15398/)

 

Virtual Thread 는 Heap 메모리에 있는 객체다. Virtual 스레드는 생성 작업이 JVM 위에서 이뤄지기 때문에 Platform Thread 에 비해서 생성 비용도 싸다. Stack 이나 IP 같은 저장공간도 JVM 내에서 컨텍스트 스위칭 되는 형태이기 때문에 불필요한 시스템 콜이 없어지게 된다

 

Java Thread 에 비해서 커널레벨까지 내려가서 실행하는 시스템 작업이 없어 효율적이다. 

 

추가로 Virtual Thread 의 강점은 Blocking I/O 를 Non blocking I/O 로 실행하게 된다는 점이다.

 

기존 Java Thread 를 사용할 경우 I/O 작업이 오래 걸리면 끝까지 기다리게 된다. 반면에 Virtual Thread 에서는 JVM 이 Blocking 을 감지하면 실행하고 있던 Virtual Thread 를 중지하고 다른 Virtual Thread 를 실행한다. 

 

그래서 하나의 Java Thread 를 사용해도 두개의 작업을 동시에 실행하는 효과를 내게 된다. 

 

public class TEST {
    private static final int TASKS = 1000;

    public static void main(String[] args) {
        long start = System.currentTimeMillis();
        ioBoundTask();
        System.out.printf("Time %dms\n", System.currentTimeMillis() - start);
    }

    private static void ioBoundTask() {
        try (ExecutorService executorService = Executors.newVirtualThreadPerTaskExecutor()) {

            for (int i = 0; i < TASKS; i++) {
                executorService.submit(() -> {
                    for (int j = 0; j < 100; j++) {
                        System.out.println("Executing Blocking Task: " + Thread.currentThread());
                        try {
                            Thread.sleep(10);
                        } catch (InterruptedException e) {
                            throw new RuntimeException(e);
                        }
                    }
                });
            }
        }
    }

 }

 

Virtual Thread 를 sleep 코드에서 blocking 이 발생하지 않아서 새로운 쓰레드 생성을 계속한다.  그래서 1000개의 쓰레드를 생성하는데 1233ms 만 소요된다

Executing Blocking Task: VirtualThread[#686]/runnable@ForkJoinPool-1-worker-5
Executing Blocking Task: VirtualThread[#482]/runnable@ForkJoinPool-1-worker-2
Executing Blocking Task: VirtualThread[#688]/runnable@ForkJoinPool-1-worker-2
Executing Blocking Task: VirtualThread[#635]/runnable@ForkJoinPool-1-worker-4
Executing Blocking Task: VirtualThread[#645]/runnable@ForkJoinPool-1-worker-8
Executing Blocking Task: VirtualThread[#937]/runnable@ForkJoinPool-1-worker-1
Executing Blocking Task: VirtualThread[#972]/runnable@ForkJoinPool-1-worker-6
Tasks took 1233ms to complete

 

newCachedThreadPool 을 사용하는 경우 Java Thread 를 생성해야 하기 때문에 시간이 더 소요되고 경우에 따라선 하드웨어 자원이 부족해 crash 가 발생하게 된다.

Executing Blocking Task: Thread[#286,pool-1-thread-259,5,main]
Executing Blocking Task: Thread[#1006,pool-1-thread-979,5,main]
Executing Blocking Task: Thread[#286,pool-1-thread-259,5,main]
Executing Blocking Task: Thread[#1006,pool-1-thread-979,5,main]
Tasks took 1760ms to complete