-
Virtual Thread 알아보기Spring/Framework 2023. 11. 29. 10:33
virtual thread 에 대한 동료들의 반응
- OO님 : spring boot 3.0 보다 virtual thread 가 포함된 spring boot 3.2 가 훨씬 강력한거 같아요
- OO님 : java 11 -> 17 6단계에 LTS 했는데, virtual thread 빨리 LTS 내보낼려고 17 -> 21 4단계만에 LTS 내보낸것 같아요
Java 21 & Spring boot 3.2 released
Spring Boot 3.2 가 지난주 출시되어 Virtual Thread 사용이 가능합니다
produtct release date memo Java 21 2023-09-20 Spring boot 3.2 2023-11-23 release note 가 오늘도 업데이트 중 JEP-444 vitual thread : https://openjdk.org/jeps/444
- JEP ( Java Enhanced Proposal)
Spring Boot 3.2 에서vitual thread 지원 : https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes#support-for-virtual-threads
Vitual thread 란?
가상 스레드란 기존 Java 스레드에 새롭게 추가되는 경량 스레드 입니다.
Projcet Loom의 결과물로 추가된 기능으로 OS 스레드를 그대로 사용하지 않고, JVM 자체적으로 내부 스케줄링을 통해 사용 할 수 있는 경량 스레드를 제공합니다. 하나의 프로세스가 수십만개 이상의 스레드를 동시에 발생 할 수 있게 설계가 되어있습니다.
출처: https://itkjspo56.tistory.com/316 [로춘남의 IT이야기:티스토리]
Virtual Thread 에 대해, Soo story blog 에서 잘 정리하셨습니다.
https://findstar.pe.kr/2023/04/17/java-virtual-threads-1/
https://findstar.pe.kr/2023/07/02/java-virtual-threads-2/
JVM thread 1 : 1 OS thread
Blocking 이 발생하면, Unmount 하고 다른 Vitual Thread 를 Carrier Thread 로 mount
virtual thread 는 cpu 를 사용하지 않는 네트워크 I/O, 파일 I/O 등등 에서 block 이 걸리면, stack frames를 heap 메모리에 저장(복사) 해놓고 unmount 시켰다가, 블로킹 메서드가 종료되면, heap 메모리에 저장된 stack frames 를 다시 virtual threads 로 불러오는데 이 과정을 mounting 이라고 부릅니다.
출처 : 오늘도 끄적끄적님 virtual thread 훑어보기 : https://perfectacle.github.io/2022/12/29/look-over-java-virtual-threads/
구분 lang.Thread가상 스레드메타 데이터 사이즈약 2kb200~300b메모리미리 할당된 Stack 사용필요시 마다 Heap 사용컨텍스트 스위칭 비용1~10us1us 미만출처 : 세바니님 virtual thread https://blog.naver.com/PostView.naver?blogId=seban21&logNo=223138158582&categoryNo=12&parentCategoryNo=7&viewDate=¤tPage=&postListTopCurrentPage=&isAfterWrite=true
성능테스트
테스트 환경
- Ubuntu 20
- Java 21 (sdkman)
- Gradle 8.4 build
- VM 인스턴스 머신 4 Core / 8 GiB memory
- 별도의 mariadb instance (max connection size 151)
- Max heap 2G
출처 Soo story : https://findstar.pe.kr/2023/07/02/java-virtual-threads-2/
@GetMapping("/") public String getThreadName() { // 단순히 스레드 이름을 반환 아무런 blocking 코드 없음 return Thread.currentThread().toString(); } @GetMapping("/block") public String getBlockedResponse() throws InterruptedException { // Thread sleep 1초 // 비지니스 로직 처리에 thread 가 blocking 되는 환경 가정 Thread.sleep(1000); return "OK"; } @GetMapping("/query") public String queryAndReturn() { // 쿼리 질의가 1초 걸린다고 가정 return jdbcTemplate.queryForList("select sleep(1);").toString(); }
1. Simple response 호출
구분 throughput virtual user Virtual Thread(1회차) 24360.88 3000 Virtual Thread(2회차) 24608.85 3000 Virtual Thread(3회차) 24455.14 3000 Platform Thread(1회차) 36085.42 3000 Platform Thread(2회차) 36396.71 3000 Platform Thread(3회차) 36107.85 3000 → 단순 호출은 안쓰는게 좋음
2. Thread.sleep(1000) - Blocking 호출
구분 throughput virtual user Virtual Thread(1회차) 2975.38 3000 Virtual Thread(2회차) 2979.87 3000 Virtual Thread(3회차) 2978.39 3000 Platform Thread(1회차) 199.78 3000 Platform Thread(2회차) 199.58 3000 Platform Thread(3회차) 199.6 3000 → blocking 이 있으면 15배 위력 발휘
3. Sleep 이 걸려 있는 쿼리 호출 (Hikari connection pool - max 150)
구분 throughput virtual user Virtual Thread(1회차) SQLTransientConnectionException 3000 Virtual Thread(2회차) SQLTransientConnectionException 3000 Virtual Thread(3회차) SQLTransientConnectionException 3000 Platform Thread(1회차) 149.26 3000 Platform Thread(2회차) 149.53 3000 Platform Thread(3회차) 149.53 3000 → db 에 사용하려면 추가 작업 필요 , tomcat 이 queue 역할을 했는데, 다 db ( hikaricp : max pool 150 ) 로 넘겼는데, 처리하지 못해 connection exception 발생
Spring Cloud 의 spring boot 3.2 지원은 RC1
Spring boot 3.2 출시에 발맞추어, 관련 product 들의 출시가 준비중 입니다.
Spring cloud 는 2023.0 부터 spring boot 3.2 를 지원하며,
현재 2023.0.0-RC1 상태입니다2024-12-06 일자로 spring cloud 2023.0 이 출시 되었습니다
https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2023.0-Release-Notes
spring cloud 의 spring boot supported versions : https://github.com/spring-cloud/spring-cloud-release/wiki/Supported-Versions
RC1 은 https://repo.spring.io/ 를 참조하면, 사용 가능 합니다.
https://repo.spring.io/ui/native/milestone/org/springframework/cloud/spring-cloud-dependencies/
Additional
virtual thread state
/* * Virtual thread state transitions: * * NEW -> STARTED // Thread.start, schedule to run * STARTED -> TERMINATED // failed to start * STARTED -> RUNNING // first run * RUNNING -> TERMINATED // done * * RUNNING -> PARKING // Thread parking with LockSupport.park * PARKING -> PARKED // cont.yield successful, parked indefinitely * PARKING -> PINNED // cont.yield failed, parked indefinitely on carrier * PARKED -> RUNNABLE // unparked, schedule to continue * PINNED -> RUNNING // unparked, continue execution on same carrier * * RUNNING -> TIMED_PARKING // Thread parking with LockSupport.parkNanos * TIMED_PARKING -> TIMED_PARKED // cont.yield successful, timed-parked * TIMED_PARKING -> TIMED_PINNED // cont.yield failed, timed-parked on carrier * TIMED_PARKED -> RUNNABLE // unparked, schedule to continue * TIMED_PINNED -> RUNNING // unparked, continue execution on same carrier * * RUNNABLE -> RUNNING // continue execution * * RUNNABLE -> BLOCKING // blocking on monitor enter * BLOCKING -> BLOCKED // blocked on monitor enter * BLOCKED -> RUNNABLE // unblocked * * RUNNING -> YIELDING // Thread.yield * YIELDING -> RUNNABLE // yield successful * YIELDING -> RUNNING // yield failed */ private static final int NEW = 0; private static final int STARTED = 1; private static final int RUNNABLE = 2; // runnable-unmounted private static final int RUNNING = 3; // runnable-mounted // untimed parking private static final int PARKING = 4; private static final int PARKED = 5; // unmounted private static final int PINNED = 6; // mounted // timed parking private static final int TIMED_PARKING = 7; private static final int TIMED_PARKED = 8; private static final int TIMED_PINNED = 9; private static final int YIELDING = 10; // Thread.yield // monitor enter private static final int BLOCKING = 11; private static final int BLOCKED = 12; private static final int TERMINATED = 99; // final state // can be suspended from scheduling when unmounted private static final int SUSPENDED = 1 << 8; // parking permit private volatile boolean parkPermit; // unblocked private volatile boolean unblocked; // carrier thread when mounted, accessed by VM private volatile Thread carrierThread; // termination object when joining, created lazily if needed private volatile CountDownLatch termination;
Three pieces of practical advice
01. Virtual Threads를 풀링하지 말고 Semaphore를 사용해라.
Virtual Threads는 생성 비용도 굉장히 싸기 때문에 굳이 풀링할 필요가 없고, 기존 방식대로 풀링하게 되면 Virtual Threads가 아닌 Platform Threads를 아마 사용하게 되는 것 같다.
따라서 만약 풀링해야하는 일이 있다면 Semaphore 방식을 권장하고 있다.02. synchronized 키워드를 사용해서 Carrier Thread까지 Blocking 되는 현상(이걸 보고 Thread가 Pinning 됐다고 말하는 듯)을 피해라
03. 쓰레드 로컬을 조심해서 사용해라
가상 스레드는 굉장히 많은 개수가 생성될수 있어 스레드 로컬은 Map에 정보를 저장하는 방식이다 보니 메모리 누수 및 잘못된 값을 가져올수도 있어서 그런거 같다.
블로거의 추가 주의 사항
가상 스레드를 사용하면 처리량(throughput)만 올라가고 응답속도는 비슷하다
Socket API 호환성
기존 코드와의 호환성을 위해
JEP 353 (Reimplement the legacy Socket API) https://openjdk.org/jeps/353
JEP 373 (Reimplement the legacy DatagramSocket API) https://openjdk.org/jeps/373
에서 Socket API들을 재구현함으로써 코드의 변경없이 가상 스레드를 사용할 수 있도록 하였다.
java 11 소켓 클래스 java.net.SocketInputStream
java 21(19이상) 소켓 클래스 sun.nio.ch.NioSocketImpl
spring boot 3.2 에서 tomcat 의 virtual thread 사용 속성
# virtual thread 사용 spring.threads.virtual.enabled: true # tomcat 이 virtual thread 사용 server.tomcat.use-virtual-threads=true
References
JEP-444 vitual thread : https://openjdk.org/jeps/444
Spring Boot 3.2 release note : https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-3.2-Release-Notes
Spring cloud 2023.0 release note : https://github.com/spring-cloud/spring-cloud-release/wiki/Spring-Cloud-2023.0-Release-Notes
Soo story virtual thread 설정https://findstar.pe.kr/2023/04/17/java-virtual-threads-1/
https://findstar.pe.kr/2023/07/02/java-virtual-threads-2/
오늘도 끄적끄적님 virtual thread : https://perfectacle.github.io/2022/12/29/look-over-java-virtual-threads/
우아한테크 : virutal thread https://youtu.be/Q1jZtN8oMnU?si=BdoaONlQ49mB4Mh7
우아한테크 : tomcat thread pool : https://youtu.be/prniILbdOYA?si=QVhuMboIwTHH9Qbx
우아한테크 : thread pool : https://youtu.be/um4rYmQIeRE?si=01oodxSzJm5FpyUM
Baeldung : https://www.baeldung.com/java-virtual-thread-vs-thread
Josh Long spring boot 3.2 : https://www.youtube.com/watch?v=dMhpDdR6nHw&t=1202s
'Spring > Framework' 카테고리의 다른 글
Vritual Thread - Kakao tech meet (1) 2023.12.12 spring boot 3.2 - restclient & declarative http interface (2) 2023.12.03 references - declarative HTTP interface (0) 2023.11.18 Spring boot 3 - Problem Details for HTTP APIs (0) 2023.11.12 References - Spring boot 3 - Problem Details for HTTP APIs (0) 2023.11.05