본 포스팅은 글또 9기 활등 중 Udemy 로부터 강의 쿠폰을 지원받아 작성되었습니다.
【한글자막】 Java 멀티스레딩, 병행성 및 성능 최적화
이번 포스팅은 ‘글또 9기’ 활동 중 일부로, Udemy에서 지원해주신 쿠폰으로 수강한 【한글자막】 Java 멀티스레딩, 병행성 및 성능 최적화 강의에 대한 후기 글 입니다. 멀티스레드의 필수(기초)이론부터 심화주제까지 폭넓게 다루고 있고, 멀티스레딩과 병행성에 대한 이론적인 학습은 물론 관련 코드를 포함한 실습과정이 잘 구성되어 있습니다. 요즘 개인적인 사정(결혼준비, 회사업무 등등…)으로 준비해야 할 것이 너무 많아 글쓰기에 충실하지 못했는데, 다시 마음을 다잡고 초심으로 돌아가야겠습니다.
들어가며
데이터 엔지니어링에 관심을 가질수록, 그 기반이 되는 백엔드 엔지니어링에도 관심이 생기게 됩니다. 회사 업무인 마이데이터와 관련된 시스템 아키텍처에도 백엔드: 그 중에서 멀티스레딩을 통한 병행성은 성능최적화에 핵심이 됩니다. 사실 멀티스레딩은 취준생 시절 떄 이론적인 공부만 얕게 해봤지 제대로된 강의를 통해 학습해 본 적은 없어 이번 기회로 멀티스레드 아키텍처
의 기초부터 성능 최적화
, ReentrantLock
, Lock-Free 알고리즘
, 비동기-논블록킹IO
와 가상스레드
등을 공부했는데 듣길 너무 잘했다는 생각이 들었습니다. 추천드리는 이유는 다음과 같습니다.
- 강사님(Michael Pogrebinsky) 설명이 굉장히 깔끔합니다.
- Thread, Runnable과 같은 정말 기본 내용으로 시작하여 심화된 개념까지 깔끔하게 정리하여 전달해줍니다.
- 불필요한 내용으로 혼란을 주지 않고 핵심만 짚어줍니다. 말씀하시는 흐름이 수강생이 이해하기 편하도록 배려하여 짜셨다는 것이 느껴졌습니다.
- 강의 구성이 개념을 익히기에 적합합니다.
- 이론을 코드로 옮겨 실습하는 과정과, 섹션 별 코딩실습 부분은 배우는 중간에 점검하고 넘어가기 좋은 구성입니다.
- 처음에 개념을 전체적으로 정리하고 설명한 개념 그대로가 옮겨진 코드 실습, 그리고 적절한 예제(코딩연습)로 개념을 재점검합니다. 특히 관련 개념을 완벽하게 짚고 넘어갈 수 있는 실무 내용을 반영한 예시를 들어 설명해줍니다.
- 실무와 밀접하게 연관된 내용을 배웁니다.
- 업무 자체에 코드 그대로 적용가능하진 않지만, 다루는 개념들이 실무에 직접적으로 연관되어 있습니다. 업무와 연관성이 있으나 의심스러웠던 부분들이 강의를 듣고 나서 훨씬 깔끔하게 정리되었습니다.
배우는 내용
강의에서 배우게 되는 내용은 다음과 같습니다.
- 운영 체제의 기본 내용 및 멀티스레딩과 병행성이 필요한 이유
- 멀티스레딩의 기본 - Java에서 스레드를 생성하는 방법 및 스레드 간에 소통하는 방법
- 멀티스레드 병렬 실행 애플리케이션의 성능 관련 고려 사항 및 설계 패턴. 지연 시간 또는 처리량을 최적화하는 방법
- Java에서 스레드 간에 데이터를 공유하는 방법. 발생할 수 있는 모든 함정과 어려움 및 솔루션과 모범 사례
- 반응성과 성능을 향상시킬 수 있는 락이 걸리지 않은 고급 알고리즘 및 데이터 구조
강의 목차로 정리하면 다음과 같습니다.
- 개요
- 스레딩 기초 - 스레드 생성
- 스레딩 기초 - 스레드 조정
- 성능 최적화
- 스레드 간 데이터 공유
- 병행성 문제와 솔루션
- 락킹 심화
- 스레드 간 통신
- Lock-Free 알고리즘, 데이터 구조 및 기술
- 고성능 IO를 위한 스레딩 모델
- 가상 스레드와 고성능 IO
- 마무리
사전 점검
강의 처음은 개요를 통해 멀티스레딩의 기본적인 이론을 먼저 다루고 시작합니다. 아래 내용은 바로 코드 실습부터 들어가고 싶으신 분들을 위해 빠르게 훑어볼 수 있도록 첫 개요 섹션을 정리한 내용입니다. 학부 때 외웠던 기억이 솔솔 나더라구요 :)
멀티스레드가 필요한 이유
- responsiveness(응답성)
- 개별 task들을 각각의 thread 가 처리함으로서 응답성을 향상시킬 수 있다.
- 이런 multitasking은
Concurrency
라고 한다. 그리고 이런 multitasking은 한개의 코어로도 구현할 수 있다.
- performance(성능)
- 단일 thread 로 구현하는 것 보다 훨씬 빠르게 정해진 기간동안 일을 해결할 수 있다.
- 마찬가지 동일한 이유로 하드웨어 수 나 서비스를 유지하는 공수가 줄어든다.
프로세스,스레드 동작 방식
- 컴퓨터 전원을 켜면 운영체제가 disk에서 메모리로 로드된다. (이때 다른 프로그램들은 파일 형태로 디스크에 있다.)
- 사용자가 프로그램을 실행시키면 해당 프로그램을 디스크에서 가져와, 인스턴스 형태로 메모리에 생성한다. (이때의 인스턴스를 ‘프로세스(Process)’ 혹은 ‘어플리케이션 컨텍스트(Context of an Application)’ 라고 한다.) (따라서 각각의 프로세스는 완전히 구분되어 있다.)
- 프로세스의 메타데이터는 다음과 같다.
- PID : 어플리케이션이 읽고 쓰기 위해 여는 파일
- Code : CPU에서 실행되는 프로그램 명령어
- Data(Heap) : 어플리케이션 실행에 필요한 모든 데이터가 들어있음
- Main Thread : 적어도 하나의 메인 스레드가 있음 (멀티 스레드 환경에서는 이
Stack
,Instruction Pointer
덩어리가 여러개 생성되는 것이고 나머지는 공유한다.)- Stack : 지역변수가 생성되고 함수가 실행되는 영역
- Instruction Pointer : 스레드가 수행할 다음 영역을 가르키는 포인터.
- 프로세스의 메타데이터는 다음과 같다.
컨텍스트 스위치(Context Switch)
- 컨텍스트 스위치의 정의
- 일반적으로 프로세스는 다른 프로세스들과 독립적으로 움직인다.
- 코어 수 보다 프로세스가 더 많고, 각 프로세스는 스레드 하나를 기본으로 가진다.
- 스레드 하나를 수행하고, 멈추고 스케줄링에 따라 다른 스레드를 수행하는 것을
Context Switch
라 한다.
- 컨텍스트 스위치의 주의점
- 동시에 많은 스레드를 다룰 때는 효율이 떨어질 수 밖에 없다. 이는 병행성의 대가이다.
- CPU에서 실행되는 각 스레드는 CPU내 레지스터, 캐쉬, 메모리의 커널 리소스를 사용하게 되는데,
Context Switch
단계에서 이를 전부 저장해두고, 다른 스레드의 데이터를 CPU와 메모리에 복원해야 하기 때문이다.
- CPU에서 실행되는 각 스레드는 CPU내 레지스터, 캐쉬, 메모리의 커널 리소스를 사용하게 되는데,
- 너무 많은 스레드를 다루게 되면
Thrashing
이 발생하게 된다. Thrashing은 운영체제가 우리가 하고 싶은 일에 시간을 할애하기보다, Context Switch 작업에 더 많은 시간을 할애하게 되는 것이다. - 한 프로세스 내의 스레드는 자원을 공유하기 때문에, 동일한 프로세스 내의 컨텍스트 스위치가 다른 프로세스 내 스레드 간의 컨텍스트 스위치보다 효율적이다.
- 동시에 많은 스레드를 다룰 때는 효율이 떨어질 수 밖에 없다. 이는 병행성의 대가이다.
운영체제의 스레드 스케줄링 방법
- First Come, First Serve : 먼저 도착한 스레드를 먼저 실행하는 방법
- 다른 thread의 기아 문제를 야기한다.
- Shortest Job First : 실행시간이 짧은 스레드를 먼저 실행한다.
- 실행시간이 긴 스레드는 영영 실행이 안될 수 있다.
- (실제)
Epochs
- 운영체제는 Epoch에 맞추어 시간을 적당한 크기로 나눈다. 그리고 각 스레드의 타임스레드를 종류별로 에폭에 할당한다. 하지만 모든 스레드가 각 에폭에서 완료되는 것은 아니다.
- (실제)
Dynamic Priority
- Dynamic-priority = Static-priority(개발자가 미리 정해둔 우선순위) +
bonus
(각 Epoch 마다OS
가 세팅한다 ex)실시간 스레드
등에 더 점수를 부여함,기아현상
역시 고려함)
- Dynamic-priority = Static-priority(개발자가 미리 정해둔 우선순위) +
Thread를 사용하는 경우, Process를 사용하는 경우
- Thread를 사용하는 multi-thread architecture
- 자원을 많이 공유하는 경우 멀티스레드 아키텍처가 유리할 것이다.
- Thread가 생성과 파괴에 훨씬 빠르며, 같은 `프로세스 내의 컨텍스트 스위치는 프로세스 간의 스위치보다 빠르다.
- Process를 사용하는 multi-process architecture
- 보안성과 안정성이 중요하다면 독립된 프로세스로 수행하는 것이 나을 수 있다.(멀티스레드 앱은 스레드 하나가 앱 전체를 다운시킬 수 있음)
- 서로 관련없는 작업을 하나의 프로세스로 통합하는 것 역시 의미가 없다.
소감
Udemy에는 참 좋은 강의들이 많이 있는 것 같습니다. 이번 강의도 꼼꼼하게 정리하여 멀티스레딩 관련된 포스팅을 연재하고자 합니다. 위 강의는 JVM, 멀티스레딩, 성능 최적화에 관심은 많으나 시간이 부족한 분들, 이론/실무를 병행하여 빠르게 학습하시고 싶은 분들께 추천드립니다.