IT/JAVA

Java Virtual Thread 경량 동시성

KeepGooing 2024. 11. 25. 16:06
반응형

Java Virtual Thread 경량 동시성

 

1. Virtual Thread의 기본 개념

Virtual Thread는 경량 스레드로, OS 스레드에 직접 매핑되지 않고 JVM에 의해 관리됩니다. 이를 통해 수백만 개의 Virtual Thread를 생성할 수 있습니다.

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

public class VirtualThreadExample {
    public static void main(String[] args) throws InterruptedException {
        Runnable task = () -> {
            System.out.println("Task executed by: " + Thread.currentThread());
        };

        // 가상 스레드 생성 및 실행
        Thread virtualThread = Thread.ofVirtual().start(task);
        virtualThread.join();

        // ExecutorService를 사용한 가상 스레드 풀
        try (ExecutorService executor = Executors.newVirtualThreadPerTaskExecutor()) {
            executor.submit(task);
        }
    }
}
        

 

팁: Virtual Thread는 기존의 Thread API와 호환되므로, 대부분의 경우 코드 변경 없이 Virtual Thread의 이점을 활용할 수 있습니다.

 

 

2. Virtual Thread vs Platform Thread

 

Virtual Thread와 Platform Thread의 주요 차이점을 이해하는 것이 중요합니다.

public class ThreadComparison {
    public static void main(String[] args) throws InterruptedException {
        // Platform Thread
        Thread platformThread = new Thread(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // Virtual Thread
        Thread virtualThread = Thread.ofVirtual().unstarted(() -> {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        long startTime = System.currentTimeMillis();
        platformThread.start();
        platformThread.join();
        long platformTime = System.currentTimeMillis() - startTime;

        startTime = System.currentTimeMillis();
        virtualThread.start();
        virtualThread.join();
        long virtualTime = System.currentTimeMillis() - startTime;

        System.out.println("Platform Thread time: " + platformTime + "ms");
        System.out.println("Virtual Thread time: " + virtualTime + "ms");
    }
}
        

 

 

팁: Virtual Thread는 특히 I/O 작업이나 네트워크 호출과 같은 블로킹 작업에서 큰 이점을 제공하지만 CPU 집약적인 작업에서는 Platform Thread가 여전히 효율적일 수 있음을 고려하세요.

 

 

3. 구조적 동시성

 

Virtual Thread와 함께 도입된 구조적 동시성은 동시성 코드의 가독성과 유지보수성을 크게 향상시킵니다.

import jdk.incubator.concurrent.StructuredTaskScope;

public class StructuredConcurrencyExample {
    public static void main(String[] args) throws Exception {
        try (var scope = new StructuredTaskScope.ShutdownOnFailure()) {
            StructuredTaskScope.Subtask task1 = scope.fork(() -> fetchData("source1"));
            StructuredTaskScope.Subtask task2 = scope.fork(() -> fetchData("source2"));

            scope.join();           // 모든 작업이 완료될 때까지 대기
            scope.throwIfFailed();  // 실패한 작업이 있으면 예외 발생

            // 결과 처리
            String result1 = task1.get();
            String result2 = task2.get();
            System.out.println("Results: " + result1 + ", " + result2);
        }
    }

    private static String fetchData(String source) throws InterruptedException {
        // 데이터 가져오기 시뮬레이션
        Thread.sleep(1000);
        return "Data from " + source;
    }
}
        

 

팁: 구조적 동시성을 사용하면 작업의 수명 주기를 명확하게 관리할 수 있으며, 예외 처리와 취소 처리가 더 간단해집니다.

 

 

4. 성능 고려사항

 

Virtual Thread는 많은 상황에서 성능 향상을 가져올 수 있지만, 모든 시나리오에 적합한 것은 아닙니다.

public class PerformanceExample {
    public static void main(String[] args) throws InterruptedException {
        int taskCount = 1_000_000;

        long startTime = System.currentTimeMillis();
        try (var executor = Executors.newVirtualThreadPerTaskExecutor()) {
            for (int i = 0; i < taskCount; i++) {
                executor.submit(() -> {
                    // 가벼운 I/O 작업 시뮬레이션
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                });
            }
        }
        long endTime = System.currentTimeMillis();

        System.out.println("Executed " + taskCount + " tasks in " + (endTime - startTime) + "ms");
    }
}
        

 

팁: Virtual Thread는 I/O 바운드 작업에 특히 효과적이지만, CPU 바운드 작업에는 기존의 스레드 풀이 더 적합할 수 있으니 어플리케이션의 특성에 따라 적절한 접근 방식을 선택하세요.

 

5. Virtual Thread 사용 시 모범 사례

  • 동기화 블록 내에서 블로킹 작업을 수행하지 마세요. 이는 Virtual Thread의 이점을 무효화할 수 있습니다.
  • ThreadLocal 사용 시 주의가 필요합니다. Virtual Thread는 ThreadLocal 값을 공유할 수 있습니다.
  • Virtual Thread를 직접 풀링하지 마세요. JVM이 이를 효율적으로 관리합니다.
  • 기존 코드베이스를 Virtual Thread로 마이그레이션할 때는 점진적으로 접근하세요.
  • 성능 테스트를 통해 Virtual Thread 도입의 실제 이점을 검증하세요.

 

 

Java Virtual Thread는 동시성 프로그래밍의 새로운 지평을 열어줍니다. 특히 I/O 바운드 작업이 많은 애플리케이션에서 큰 성능 향상을 기대할 수 있습니다. 그러나 모든 상황에 적합한 만능 해결책은 아니므로, 애플리케이션의 특성과 요구사항을 고려하여 적절히 활용해야 합니다. Virtual Thread와 구조적 동시성을 효과적으로 사용하면, 더 간결하고 효율적인 동시성 코드를 작성할 수 있습니다.

 

 

 

 

마지막 팁: Virtual Thread는 아직 진화 중인 기술입니다. Java의 새로운 버전이 출시될 때마다 관련 기능과 최적화가 추가될 수 있으므로, 지속적인 학습과 실험이 중요합니다. 또한, 기존의 동시성 프레임워크와 라이브러리들이 Virtual Thread를 지원하도록 업데이트되고 있으니, 사용 중인 라이브러리의 호환성을 항상 확인하는 습관을 들이는게 중요해요!


반응형