개발놀이터
자바는 스레드 생성비용이 어느정도길래 스레드 풀을 둘까? 본문
안녕하세요, 이번 포스팅에선 스레드 풀의 존재 의의를 스레드 생성 비용의 관점에서 공부해보고 정리했습니다!
흔히 알기로 데이터베이스 커넥션, JVM의 스레드의 생성비용이 크기 때문에 커넥션 풀, 스레드 풀을 적극 사용한다고 알려져있습니다. 이런 내용의 포스팅은 많이 있지만 이 스레드의 생성 비용이 정확히 어느정도인지 알려주는 곳은 없더군요.
저와 같은 생각을 가지신 분이 "널널한 개발자" 유튜버분에게 질문을 올리시고 그에 대한 답변 영상에 영감을 얻어 공부했습니다.
운영체제가 관리하는 스레드
스레드는 프로세스에 속해있는 실행 단위이죠. 보통 운영체제가 프로세스를 PID라는 것으로 관리하고 이 프로세스는 컴퓨터의 파일 (여기서 파일은 소켓이 될 수도 있고, 실제 파일이 될 수도 있고 다양합니다.) 을 읽거나, 쓰거나, 실행하도록 되어있습니다.
이렇게 OS가 프로세스를 PID로 관리하고 프로세스는 그 안에서 스레드를 생성해서 작업을 진행합니다.
각각의 프로세스는 OS가 작업의 정도에 따라 메모리를 할당해주고 스레드가 그 메모리 자원을 공유하면서 실행이 되는 것은 익히 잘 알려진 사실입니다.
이 상황에서 만약 프로세스가 OS에게 "나 메모리 더 필요해! 메모리 더 줘!" 같은 상황이 온다면 어떻게 될까요?
물론 OS는 메모리를 할당해줄겁니다. 그러면서 프로세스는 일이 늘어난 만큼 스레드를 추가로 생성할것인데요. 우리는 이 부분을 잘 뜯어봐야합니다.
Thread Control Block
Thread Control Block (이하 TCB) 는 운영체제가 스레드를 잘 관리할 수 있도록 만든 자료구조입니다. 이 TCB에는 스레드의 식별자와 스레드 이름, 이 스레드가 어떤 프로세스에 속해있는지, 스레드의 상태 등 스레드의 각종 정보들이 들어있습니다.
이와 마찬가지로 Process Control Block (이하 PCB) 도 비슷한 방식으로 동작합니다.
아무튼 스레드를 생성하면 OS가 무슨 일을 해야하는지 간단하게만 정리해보겠습니다.
- TCB에 등록
- PCB에도 등록
- TCB에 등록함과 동시에 프로세스에 속한 시스템 자원도 할당 (메모리 등)
- 등등..
이 작업은 사실 애플리케이션 레벨에서 개발하고 있는 서비스 개발자들한테는 큰 작업이 아닐 수도 있습니다.
"에게.. 그냥 슥삭 하면 되는거지 뭐가 어렵다고"
하지만 이는 절대 작은 비용이 아닙니다.
예를 들어서 제가 오마카세 식당을 크게 운영하고 있다고 가정해보겠습니다.
오마카세 식당에서 요리사 한명이 처리할 수 있는 손님의 수는 정해져있습니다. 저희 오마카세는 요리사 5명이 있고 각각의 요리사당 최대 5명의 손님을 대접할 수 있다고 가정해봅시다.
그럼 총 25명의 손님을 맞이할 수 있을건데 여기서 비현실적인 가정을 하나 하자면 저희 오마카세는 예약제가 아닙니다.
자 이런 상황에서 기존엔 20명 남짓 오던 손님이 갑자기 50명이 몰려들기 시작했습니다.
그럼 요리사를 더 뽑아야겠죠? 요리사를 더 뽑는게 스레드를 생성하는 로직과 비슷합니다.
- 우선 요리사 지원 서류를 받아야하구요
- 서류도 읽어봐야하고..
- 서류 통과하면 면접도 봐야하고..
- 면접보고 합격하면 계약서도 써야하고..
- 계약서 다쓰고 옷이나 칼같은 도구도 준비해야하고..
지금 손님이 바글바글해서 식당이 꽉찼는데 주인인 저는 머리가 터져버리기 직전일겁니다.
그래서 이런 스레드를 미리 생성해둔다는 전략이 나온 것입니다. 스레드의 생성 비용이 크니까 미리 스레드를 만들어두고 반납하고 재사용하는 로직을 생각한겁니다.
JVM에서의 스레드
JVM에서는 스레드를 미리 생성해두고 이 스레드를 요청에 맞게 분배하고 다시 반납하면 재사용할 수 있습니다. JVM의 스레드와 일반적으로 말하는 OS상에서의 물리 스레드는 다르긴 하지만 JVM 스레드도 결국 이 물리 스레드를 기반으로 만드는 것이기 때문에 연관이 없는 것은 아닙니다.
이에 대한 내용이 아래의 링크에 잘 정리되어있습니다!
https://coding-review.tistory.com/520
흔히 JVM진영에선 플랫폼 스레드라고 부르는 것이 바로 운영체제의 스레드를 가져와서 만드는 것입니다. 스레드 스케줄링을 이용해서만들죠.
서비스 개발자들은 이 스레드 풀을 적절히 조절해서 애플리케이션의 성능을 어느정도 관리할 수 있습니다.
그런데 여기서 잠깐!
스레드 풀, 보통 어느정도로 설정하시나요?
128, 256처럼 간단하게 설정하는 경우도 많은 것으로 알고 있습니다. 하지만 이 스레드 풀을 설정하는 공식이 있다는거 알고계셨나요?
스레드 풀의 총량은 CPU의 코어 수에 CPU 사용시간의 역수를 나눈 값이 적정량이라고 하는데..
CPU 코어는 알겠는데 CPU 사용시간은 뭐야?
CPU 사용시간은 [특정 요청이 걸린 시간 + 운영체제가 I/O를 작업하는데 걸린 시간] 입니다.
예를 들어서 코어가 2코어이고 특정 요청이 걸린 시간이 0.1초, 운영체제가 I/O를 작업한 시간이 0.05초라면 스레드 풀의 적정량은 13이고 대충 15정도 하면 되겠네요.
이 때 주의해야하는 점은 만약 커넥션 풀이 10인경우 스레드 풀을 15로 설정해두어도 10개만 스레드가 동작하기 때문에 스레드 풀과 커넥션 풀을 잘 조절해서 설정해야한다는 것입니다.
스레드 풀의 총량은 API성능 테스트를 하면서 요청 시간이 가장 오래 걸린 것을 기준으로 하면 될 것 같네요.
마치며
이번 포스팅에선 스레드 풀의 존재 의의를 스레드 생성 비용의 관점에서 알아보고 정리해봤습니다. 스레드의 생성 비용이 커 스레드 풀을 운용한다는 것은 알고 있었지만 이정도로 비용이 클 줄은 몰랐네요.. 괜히 스레드 풀을 운용하는게 아닌 것 같습니다.
이 포스팅에선 프로세스와 스레드의 컨텍스트 스위칭 비용에 대해서는 다루지 않았습니다. 물론 중요한 내용이지만 본문의 내용과 연관이 없어서 내용을 간결하게 작성하기 위해 빼버렸습니다.
긴 글 읽어주셔서 감사합니다! 오늘도 즐거운 하루 되세요~
'Java' 카테고리의 다른 글
자바의 미래 : 가상 스레드 (0) | 2024.06.25 |
---|---|
Java8 StreamAPI (1) | 2024.04.28 |
ConcurrentMap, ConcurrentHashMap (0) | 2023.02.18 |
상속과 super 그리고 super() (0) | 2022.09.15 |
final 키워드 (0) | 2022.08.04 |