티스토리 뷰

JVM/JAVA

[Java] Thread 이해해보기

글을 쓰는 개발자 2022. 10. 10. 08:34
반응형

글을 앞서 소개하기 앞서서 글을 적는 데 도움이 되었던 사이트 링크를 먼저 남깁니다.

https://bugs.openjdk.org/browse/JDK-8277131

https://github.com/spring-projects/spring-framework/issues/23443

https://spring.io/blog/2022/10/11/embracing-virtual-threads

 

Java Thread 의 변화

Loom Project 가 JDK 19 에 들어가면서 Virtual Thread, Concurrency 에 대한 내용이 추가가 되었다.

이전 버전에서의 Java 에서는 Virtual Thread 가 아닌 OS Thread를 생성하는 형식으로 구성되어 있었다.

기본적으로 Virtual Thread 특성은 가벼운 스레드로서 생성하는 비용을 많이 줄일 수 있다는 특성이 있으며 concurrent 애플리케이션의 높은 처리량이 관찰된다.

그렇다고 이번 패치는 자바의 concurrency model을 바꾸는 것은 아니며 병행처리에 대한 자바 model을 바꾸는 것은 아니다.

 

이유

기본적으로 자바는 멀티 스레드이며, 각각의 스레드는 지역 변수 저장하거나 메소드 call을 조정하기 위한 스택을 제공해줬다. 그 뿐만 아니라 Exception이 같은 스레드에서 나거나 했을 때 같은 context를 공유하며 개발자는 이를 통해 어떤 일이 일어났는 지 스레드의 call stack을 debugging 하는 데 용이한 구조를 가졌다.

 

기본적으로 서버 애플리케이션은 유저 요청에 따라 독립적으로 동시에 다루며, 이와 같이 구성하기 위해서는 해당 요청을 스레드와 묶어 하나의 스레드가 하나의 요청을 처리하도록 구성되어 있다.

 

이 말을 thread-per-request style 이라 하며 이러한 방식은 이해하기 쉬우며, 프로그램하기 쉽다.

Thread-Per-Request

 

서버 어플리케이션의 확장성은 Little's law 의해 처리되며, 해당 법칙은 latency, concurrency, 그리고 처리량과 관련되어 있다.  

예를 한 번 들어보자. 평균 latency가 50ms 이고 초당 200개의 요청을 처리한다고 했을 때 동시에 처리할 수 있는 갯수는 10개의 요청들이다.

20 * x = 200  

여기서 20 은 50ms을 1초 기준으로 환산한 값이며 x는 Little's Law 에서의 람다 역할, 200은 L 을 의미한다.

이 때 초당 처리량이 2000개, 20000개 점점 늘어날수록 x의 값은 커져갈 것이다. (초당 처리량의 값이 커진다는 의미)

 

불행하게도, JDK에 구현된 스레드는 OS 스레드로 구성되어 있어서 이용가능한 스레드의 수는 제한된다. 

OS 스레드는 비용이 비싸서 우리는 해당 스레드를 많이 생성할 수 없으며, 이러한 상황은 thread-per-style 과 부적절하지 않다.

왜냐하면 숫자가 늘어남에 따라 CPU 나 네트워크 연결에 대하여 제한적이게 된다. 그래서 이러한 것을 다른 방식으로 풀어냈는데 우리가 흔히 아는 pooling 방식으로 스레드 생성에 대한 비용을 줄이고 스레드의 총 갯수를 증가하지 않는 방식으로 되어 있다.

 

Asynchronous

 

하지만 우리는 안다. thread-per-request 방식 말고 다른 방식이 있다는 것을 바로 asynchronous style 방식이 있다.

asynchrous 방식은 thread-per-style 과 같이 하나의 스레드로 처음부터 끝까지 하는 것에 비해,  fine-grained sharing of threads(스레드의 세분화된 공유)는 오직 수행할 때에만 스레드를 수행하며, 이러한 방식은 많은 스레드 사용 없이 많은 수의 요청건을 처리한다.  OS Thread 부족함으로 인한 처리량의 제한은 제거되지만, 비용은 비싸다. 왜냐하면 흔히 아는 asynchronous style을 알아야 한다. 이 말은 람다 표현식, 순차적 파이프 형식을 구성해야 한다.

 

비동기 방식은 각 순간마다 다른 스레드를 사용하기 때문에 하나의 스레드에 여러 개의 요청에 대한 콜 스택이 쌓이게 된다. 이러한 방식은 Stack trace가 불 필요하게 된다는 것을 의미한다. 이유는 디버깅하기 힘든 구조를 가졌기 때문이다.

 

이야기 하고자 하는 것을 이제 말하고자 한다. 바로 virtual thread가 도입된 방식이다.

Virtual Thread

애플리케이션이 기존 방식을 유지하며 더 확장성 있게 가능하며 효율적으로 바꿔준다.

운영체제는 한정된 램보다 더 많은 크기의 메모리가 존재한다고 생각하게 하며 이러한 방식은 Java 런타임 때 OS Thread 방식보다 더 많다고 환상을 주게 된다.

 

Virtual thread는 비동기 방식과 유사하게 OS Thread를 연산할 때에만 소비한다.

Virtual Thread는 생성하기 쉽고 가벼우며 많은 양을 생성할 수 있는 스레드다.

 

그래서 이전과 다르게 주의할 점이 있다. 바로 pooling을 해서는 안된다는 것이다.

새로운 virtual thread는 요청마다 생성된다. 대부분 스레드들은 짧은 수명과 얇은 콜 스택을 가진다. 사용 예는 http 요청이나 JDBC Query Call 이다. 이와 다르게 Platform Thread는 무겁고 비싸며 반드시 pooling 방식을 유지해야한다.

 

 

One OS Thread = Many Virtual Thread at time
즉 하나의 OS Thread는 여러개의 Virtual Thread와 연관되며 공유하는 관계다.

요약하자면 다음과 같다.

  • virtual thread는 OS Thread보다 결코 빠른 것은 아니다.
  • 동시에 많은 요청을 처리해야하는 상황에서 유리하다.

Pooling 하지 않는다는 점에서도 많은 이점을 얻을 수 있다.

왜냐하면 virtual thread는 스레드를 공유하면서 사용하지 않고 요청마다 생성하는 형태로 사용하기 때문에 ThreadLocal에 값을 저장하여 사용할 때 다른 요청에서 쓸 걱정하지 않아도 된다는 큰 이점이 있기 때문이다.

 

virtual thread의 스택은 자바의 GC heap에 저장된다. 애플리케이션이 동작하면서 해당 스택은 줄거나 늘어날 수 있다.

 

그리고 virtual thread는 많은 상황에서 스택을 변경하고 재사용할 수 있는 반면 asynchronous pipeline은 항상 새 객체를 할당해야 하므로 virtual thread는 더 적은 할당이 필요하다.

 

공식문서에 의하면 시간이 지남에 따라 virtual thread 스택의 내부 표현이 훨씬 더 간결해질 것으로 예상한다고 한다.

 

Unlike platform thread stacks, virtual thread stacks are not GC roots, so the references contained in them are not traversed in a stop-the-world pause by garbage collectors, such as G1, that perform concurrent heap scanning.

virtual thread stack은 가비지 수집기에 의한 stop the world 일시중지에서 순회되지 않는다.
 since the virtual thread can never be interrupted or unblocked. Of course, the virtual thread will not be garbage collected if it is running or if it is blocked and could ever be unblocked.

virtual thread는 결코 interrupt 나 unblock을 일으키지 않는다.

 

Little's Law

 L = λW

L : 총 처리량

λ: 대기열에 있는 요청 수 

W: 대기 시간 

반응형
반응형
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/05   »
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 31
글 보관함